@atlashub/smartstack 3.21.0 → 3.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/dist/chunks/{AgentSkillsPage-7si3Ng8e.js → AgentSkillsPage-BWQSCYl-.js} +2 -2
  2. package/dist/chunks/{AgentSkillsPage-7si3Ng8e.js.map → AgentSkillsPage-BWQSCYl-.js.map} +1 -1
  3. package/dist/chunks/{AgentSkillsPage-D0cD1QdM.js → AgentSkillsPage-IQcMnBaD.js} +2 -2
  4. package/dist/chunks/{AgentSkillsPage-D0cD1QdM.js.map → AgentSkillsPage-IQcMnBaD.js.map} +1 -1
  5. package/dist/chunks/{AgentWorkloadPage-H_7ze33H.js → AgentWorkloadPage-DqrjkvWL.js} +2 -2
  6. package/dist/chunks/{AgentWorkloadPage-H_7ze33H.js.map → AgentWorkloadPage-DqrjkvWL.js.map} +1 -1
  7. package/dist/chunks/{AgentWorkloadPage-D4d86cdV.js → AgentWorkloadPage-w-HiyFYP.js} +2 -2
  8. package/dist/chunks/{AgentWorkloadPage-D4d86cdV.js.map → AgentWorkloadPage-w-HiyFYP.js.map} +1 -1
  9. package/dist/chunks/{ApiCatalogDetailPage-2ktkRrCb.js → ApiCatalogDetailPage-D3L8Yf4G.js} +3 -3
  10. package/dist/chunks/{ApiCatalogDetailPage-2ktkRrCb.js.map → ApiCatalogDetailPage-D3L8Yf4G.js.map} +1 -1
  11. package/dist/chunks/{ApiCatalogDetailPage-BQ53xuwD.js → ApiCatalogDetailPage-MPT3Kz6H.js} +2 -2
  12. package/dist/chunks/{ApiCatalogDetailPage-BQ53xuwD.js.map → ApiCatalogDetailPage-MPT3Kz6H.js.map} +1 -1
  13. package/dist/chunks/{ApiCatalogPage-BEqTDJz8.js → ApiCatalogPage-D4Hg3uiS.js} +2 -2
  14. package/dist/chunks/{ApiCatalogPage-BEqTDJz8.js.map → ApiCatalogPage-D4Hg3uiS.js.map} +1 -1
  15. package/dist/chunks/{ApiCatalogPage-BBkWSLI8.js → ApiCatalogPage-DRg5Cz0r.js} +2 -2
  16. package/dist/chunks/{ApiCatalogPage-BBkWSLI8.js.map → ApiCatalogPage-DRg5Cz0r.js.map} +1 -1
  17. package/dist/chunks/{ApplicationDetailPage-BYJ2YMPq.js → ApplicationDetailPage-Caizuyn2.js} +2 -2
  18. package/dist/chunks/{ApplicationDetailPage-BYJ2YMPq.js.map → ApplicationDetailPage-Caizuyn2.js.map} +1 -1
  19. package/dist/chunks/{ApplicationDetailPage-D8-bf1as.js → ApplicationDetailPage-CuCW6aMB.js} +4 -4
  20. package/dist/chunks/{ApplicationDetailPage-D8-bf1as.js.map → ApplicationDetailPage-CuCW6aMB.js.map} +1 -1
  21. package/dist/chunks/{ApplicationsDashboardPage-BBlLms2r.js → ApplicationsDashboardPage-B2MW8-Kc.js} +2 -2
  22. package/dist/chunks/{ApplicationsDashboardPage-BBlLms2r.js.map → ApplicationsDashboardPage-B2MW8-Kc.js.map} +1 -1
  23. package/dist/chunks/{ApplicationsDashboardPage-DTWZxJJM.js → ApplicationsDashboardPage-BDIjFIYZ.js} +3 -3
  24. package/dist/chunks/{ApplicationsDashboardPage-DTWZxJJM.js.map → ApplicationsDashboardPage-BDIjFIYZ.js.map} +1 -1
  25. package/dist/chunks/{ApplicationsGridPage-BQaMsK1K.js → ApplicationsGridPage-DV-FihKj.js} +2 -2
  26. package/dist/chunks/{ApplicationsGridPage-BQaMsK1K.js.map → ApplicationsGridPage-DV-FihKj.js.map} +1 -1
  27. package/dist/chunks/{ApplicationsGridPage-DbVcvezt.js → ApplicationsGridPage-DXsTfXPI.js} +2 -2
  28. package/dist/chunks/{ApplicationsGridPage-DbVcvezt.js.map → ApplicationsGridPage-DXsTfXPI.js.map} +1 -1
  29. package/dist/chunks/{ApplicationsListPage-DYKM2Yeo.js → ApplicationsListPage--CGkyBuJ.js} +2 -2
  30. package/dist/chunks/{ApplicationsListPage-DYKM2Yeo.js.map → ApplicationsListPage--CGkyBuJ.js.map} +1 -1
  31. package/dist/chunks/{ApplicationsListPage-C91v2rZt.js → ApplicationsListPage-JUX823bh.js} +2 -2
  32. package/dist/chunks/{ApplicationsListPage-C91v2rZt.js.map → ApplicationsListPage-JUX823bh.js.map} +1 -1
  33. package/dist/chunks/{ApplicationsPage-BCbgotIx.js → ApplicationsPage-6zgFye6w.js} +2 -2
  34. package/dist/chunks/{ApplicationsPage-BCbgotIx.js.map → ApplicationsPage-6zgFye6w.js.map} +1 -1
  35. package/dist/chunks/{ApplicationsPage-CW3-Hjlu.js → ApplicationsPage-CQPuuiO6.js} +4 -4
  36. package/dist/chunks/{ApplicationsPage-CW3-Hjlu.js.map → ApplicationsPage-CQPuuiO6.js.map} +1 -1
  37. package/dist/chunks/{AssignmentRulesPage-D8vfGDBN.js → AssignmentRulesPage-CFffeEbo.js} +2 -2
  38. package/dist/chunks/{AssignmentRulesPage-D8vfGDBN.js.map → AssignmentRulesPage-CFffeEbo.js.map} +1 -1
  39. package/dist/chunks/{AssignmentRulesPage-CxktlEMB.js → AssignmentRulesPage-D78UeUId.js} +2 -2
  40. package/dist/chunks/{AssignmentRulesPage-CxktlEMB.js.map → AssignmentRulesPage-D78UeUId.js.map} +1 -1
  41. package/dist/chunks/{AssignmentsPage-DmfBYQAD.js → AssignmentsPage-Cww2ifZF.js} +2 -2
  42. package/dist/chunks/{AssignmentsPage-DmfBYQAD.js.map → AssignmentsPage-Cww2ifZF.js.map} +1 -1
  43. package/dist/chunks/{AssignmentsPage-sRCCBmRc.js → AssignmentsPage-DE_QS2LO.js} +2 -2
  44. package/dist/chunks/{AssignmentsPage-sRCCBmRc.js.map → AssignmentsPage-DE_QS2LO.js.map} +1 -1
  45. package/dist/chunks/{AuthCallbackPage-C7XiZxKb.js → AuthCallbackPage-CA2nO6DG.js} +2 -2
  46. package/dist/chunks/{AuthCallbackPage-C7XiZxKb.js.map → AuthCallbackPage-CA2nO6DG.js.map} +1 -1
  47. package/dist/chunks/{AuthCallbackPage-BCe_bwJM.js → AuthCallbackPage-CDUAoX-N.js} +2 -2
  48. package/dist/chunks/{AuthCallbackPage-BCe_bwJM.js.map → AuthCallbackPage-CDUAoX-N.js.map} +1 -1
  49. package/dist/chunks/{ConfirmEmailPage-BUfGSqxF.js → ConfirmEmailPage-BqsILAYH.js} +2 -2
  50. package/dist/chunks/{ConfirmEmailPage-BUfGSqxF.js.map → ConfirmEmailPage-BqsILAYH.js.map} +1 -1
  51. package/dist/chunks/{ConfirmEmailPage-Buj4x-rx.js → ConfirmEmailPage-INeHCuMB.js} +2 -2
  52. package/dist/chunks/{ConfirmEmailPage-Buj4x-rx.js.map → ConfirmEmailPage-INeHCuMB.js.map} +1 -1
  53. package/dist/chunks/{CreateSupportTicketPage-CKDX_HQm.js → CreateSupportTicketPage-BWeuV2aU.js} +2 -2
  54. package/dist/chunks/{CreateSupportTicketPage-CKDX_HQm.js.map → CreateSupportTicketPage-BWeuV2aU.js.map} +1 -1
  55. package/dist/chunks/{CreateSupportTicketPage-0LgY-_pu.js → CreateSupportTicketPage-OBwF4v7b.js} +2 -2
  56. package/dist/chunks/{CreateSupportTicketPage-0LgY-_pu.js.map → CreateSupportTicketPage-OBwF4v7b.js.map} +1 -1
  57. package/dist/chunks/{DashboardPage-CUZ80NGV.js → DashboardPage-CKHqWrdS.js} +3 -3
  58. package/dist/chunks/{DashboardPage-CUZ80NGV.js.map → DashboardPage-CKHqWrdS.js.map} +1 -1
  59. package/dist/chunks/{DashboardPage-CaNOAstg.js → DashboardPage-COmc9b__.js} +3 -3
  60. package/dist/chunks/{DashboardPage-CaNOAstg.js.map → DashboardPage-COmc9b__.js.map} +1 -1
  61. package/dist/chunks/{DashboardPage-B48_rQFi.js → DashboardPage-CfKZHiSj.js} +2 -2
  62. package/dist/chunks/{DashboardPage-B48_rQFi.js.map → DashboardPage-CfKZHiSj.js.map} +1 -1
  63. package/dist/chunks/{DashboardPage-CUZV1J9t.js → DashboardPage-CwEZZ3jx.js} +2 -2
  64. package/dist/chunks/{DashboardPage-CUZV1J9t.js.map → DashboardPage-CwEZZ3jx.js.map} +1 -1
  65. package/dist/chunks/{EscalationConfigPage-CdzAbnGy.js → EscalationConfigPage--7lgZ0kJ.js} +2 -2
  66. package/dist/chunks/{EscalationConfigPage-CdzAbnGy.js.map → EscalationConfigPage--7lgZ0kJ.js.map} +1 -1
  67. package/dist/chunks/{EscalationConfigPage-CYGIl_e6.js → EscalationConfigPage-DPyiBcqV.js} +2 -2
  68. package/dist/chunks/{EscalationConfigPage-CYGIl_e6.js.map → EscalationConfigPage-DPyiBcqV.js.map} +1 -1
  69. package/dist/chunks/{ForceChangePasswordPage-lRpkwcX7.js → ForceChangePasswordPage-BE-6umub.js} +2 -2
  70. package/dist/chunks/{ForceChangePasswordPage-lRpkwcX7.js.map → ForceChangePasswordPage-BE-6umub.js.map} +1 -1
  71. package/dist/chunks/{ForceChangePasswordPage-CvmYAV3r.js → ForceChangePasswordPage-CnsYoWmV.js} +2 -2
  72. package/dist/chunks/{ForceChangePasswordPage-CvmYAV3r.js.map → ForceChangePasswordPage-CnsYoWmV.js.map} +1 -1
  73. package/dist/chunks/{ForgotPasswordPage-0u49E4Pw.js → ForgotPasswordPage-CSq4DnFF.js} +2 -2
  74. package/dist/chunks/{ForgotPasswordPage-0u49E4Pw.js.map → ForgotPasswordPage-CSq4DnFF.js.map} +1 -1
  75. package/dist/chunks/{ForgotPasswordPage-CxQUqKOm.js → ForgotPasswordPage-DZLVolAC.js} +2 -2
  76. package/dist/chunks/{ForgotPasswordPage-CxQUqKOm.js.map → ForgotPasswordPage-DZLVolAC.js.map} +1 -1
  77. package/dist/chunks/{GroupDetailPage-DFBvVO1S.js → GroupDetailPage-Bf9Wb_2j.js} +5 -5
  78. package/dist/chunks/{GroupDetailPage-DFBvVO1S.js.map → GroupDetailPage-Bf9Wb_2j.js.map} +1 -1
  79. package/dist/chunks/{GroupDetailPage-B2FkKrGG.js → GroupDetailPage-R-hf3rJ7.js} +2 -2
  80. package/dist/chunks/{GroupDetailPage-B2FkKrGG.js.map → GroupDetailPage-R-hf3rJ7.js.map} +1 -1
  81. package/dist/chunks/{MyAccessRequestsPage-C9IX4c0K.js → MyAccessRequestsPage-BIisvWM6.js} +2 -2
  82. package/dist/chunks/{MyAccessRequestsPage-C9IX4c0K.js.map → MyAccessRequestsPage-BIisvWM6.js.map} +1 -1
  83. package/dist/chunks/{MyAccessRequestsPage-D6pVULNM.js → MyAccessRequestsPage-BLSV7Tbx.js} +2 -2
  84. package/dist/chunks/{MyAccessRequestsPage-D6pVULNM.js.map → MyAccessRequestsPage-BLSV7Tbx.js.map} +1 -1
  85. package/dist/chunks/{MyTenantsPage-BEcYYdGR.js → MyTenantsPage-D-7k9CP1.js} +3 -3
  86. package/dist/chunks/{MyTenantsPage-BEcYYdGR.js.map → MyTenantsPage-D-7k9CP1.js.map} +1 -1
  87. package/dist/chunks/{MyTenantsPage-D9f85zjF.js → MyTenantsPage-DqGW6aDt.js} +2 -2
  88. package/dist/chunks/{MyTenantsPage-D9f85zjF.js.map → MyTenantsPage-DqGW6aDt.js.map} +1 -1
  89. package/dist/chunks/{MyTicketsPage-DJR8h6y1.js → MyTicketsPage--DgDsnZA.js} +2 -2
  90. package/dist/chunks/{MyTicketsPage-DJR8h6y1.js.map → MyTicketsPage--DgDsnZA.js.map} +1 -1
  91. package/dist/chunks/{MyTicketsPage-DiOUExKJ.js → MyTicketsPage-CqJ3Aqob.js} +2 -2
  92. package/dist/chunks/{MyTicketsPage-DiOUExKJ.js.map → MyTicketsPage-CqJ3Aqob.js.map} +1 -1
  93. package/dist/chunks/{NavigationAppsPage-CeHbxfZw.js → NavigationAppsPage-Bebis_RT.js} +2 -2
  94. package/dist/chunks/{NavigationAppsPage-CeHbxfZw.js.map → NavigationAppsPage-Bebis_RT.js.map} +1 -1
  95. package/dist/chunks/{NavigationAppsPage-If7tmCFY.js → NavigationAppsPage-THNPOAjv.js} +2 -2
  96. package/dist/chunks/{NavigationAppsPage-If7tmCFY.js.map → NavigationAppsPage-THNPOAjv.js.map} +1 -1
  97. package/dist/chunks/{NotificationsPage-C29Lln5o.js → NotificationsPage-CAbNW_Cn.js} +2 -2
  98. package/dist/chunks/{NotificationsPage-C29Lln5o.js.map → NotificationsPage-CAbNW_Cn.js.map} +1 -1
  99. package/dist/chunks/{NotificationsPage-BiaLRb0s.js → NotificationsPage-DxwizUhL.js} +2 -2
  100. package/dist/chunks/{NotificationsPage-BiaLRb0s.js.map → NotificationsPage-DxwizUhL.js.map} +1 -1
  101. package/dist/chunks/{OnboardingWizardPage-DQrBKNBq.js → OnboardingWizardPage-C6HlbJ3K.js} +2 -2
  102. package/dist/chunks/{OnboardingWizardPage-DQrBKNBq.js.map → OnboardingWizardPage-C6HlbJ3K.js.map} +1 -1
  103. package/dist/chunks/{OnboardingWizardPage-BQah4cI8.js → OnboardingWizardPage-CyC2zONO.js} +2 -2
  104. package/dist/chunks/{OnboardingWizardPage-BQah4cI8.js.map → OnboardingWizardPage-CyC2zONO.js.map} +1 -1
  105. package/dist/chunks/{PermissionDetailPage-Ckjdjvf9.js → PermissionDetailPage-BDHiNgky.js} +2 -2
  106. package/dist/chunks/{PermissionDetailPage-Ckjdjvf9.js.map → PermissionDetailPage-BDHiNgky.js.map} +1 -1
  107. package/dist/chunks/{PermissionDetailPage-Dh8v7mGj.js → PermissionDetailPage-C5K17ydY.js} +2 -2
  108. package/dist/chunks/{PermissionDetailPage-Dh8v7mGj.js.map → PermissionDetailPage-C5K17ydY.js.map} +1 -1
  109. package/dist/chunks/{PermissionsPage-l0PnY-EE.js → PermissionsPage-COI5LJPo.js} +2 -2
  110. package/dist/chunks/{PermissionsPage-l0PnY-EE.js.map → PermissionsPage-COI5LJPo.js.map} +1 -1
  111. package/dist/chunks/{PermissionsPage-DLy9U3P3.js → PermissionsPage-CkOwH2_d.js} +2 -2
  112. package/dist/chunks/{PermissionsPage-DLy9U3P3.js.map → PermissionsPage-CkOwH2_d.js.map} +1 -1
  113. package/dist/chunks/{PortalDashboardPage-DFBx38-x.js → PortalDashboardPage-CoEC4CmC.js} +2 -2
  114. package/dist/chunks/{PortalDashboardPage-DFBx38-x.js.map → PortalDashboardPage-CoEC4CmC.js.map} +1 -1
  115. package/dist/chunks/{PortalDashboardPage-rQYhrX0q.js → PortalDashboardPage-DrYymEf-.js} +2 -2
  116. package/dist/chunks/{PortalDashboardPage-rQYhrX0q.js.map → PortalDashboardPage-DrYymEf-.js.map} +1 -1
  117. package/dist/chunks/{PreferencesPage-BBu8yZQB.js → PreferencesPage-CJRaU3ba.js} +2 -2
  118. package/dist/chunks/{PreferencesPage-BBu8yZQB.js.map → PreferencesPage-CJRaU3ba.js.map} +1 -1
  119. package/dist/chunks/{PreferencesPage-B81MsNV1.js → PreferencesPage-Cqr9mAab.js} +2 -2
  120. package/dist/chunks/{PreferencesPage-B81MsNV1.js.map → PreferencesPage-Cqr9mAab.js.map} +1 -1
  121. package/dist/chunks/{ProfilePage-DDrl10zj.js → ProfilePage-BZVpg6-l.js} +2 -2
  122. package/dist/chunks/{ProfilePage-DDrl10zj.js.map → ProfilePage-BZVpg6-l.js.map} +1 -1
  123. package/dist/chunks/{ProfilePage-DPoXwdnc.js → ProfilePage-Cu_FITeL.js} +2 -2
  124. package/dist/chunks/{ProfilePage-DPoXwdnc.js.map → ProfilePage-Cu_FITeL.js.map} +1 -1
  125. package/dist/chunks/{ReferencesManagementPage-eFsKjIEK.js → ReferencesManagementPage-DUlVk9Ps.js} +3 -3
  126. package/dist/chunks/{ReferencesManagementPage-eFsKjIEK.js.map → ReferencesManagementPage-DUlVk9Ps.js.map} +1 -1
  127. package/dist/chunks/{ReferencesManagementPage-DhVsuElE.js → ReferencesManagementPage-ZCuYtqd7.js} +2 -2
  128. package/dist/chunks/{ReferencesManagementPage-DhVsuElE.js.map → ReferencesManagementPage-ZCuYtqd7.js.map} +1 -1
  129. package/dist/chunks/{RegisterPage-CiQib3-6.js → RegisterPage-C4xmVwh9.js} +2 -2
  130. package/dist/chunks/{RegisterPage-CiQib3-6.js.map → RegisterPage-C4xmVwh9.js.map} +1 -1
  131. package/dist/chunks/{RegisterPage-bXCcJD88.js → RegisterPage-DGyzoIHT.js} +2 -2
  132. package/dist/chunks/{RegisterPage-bXCcJD88.js.map → RegisterPage-DGyzoIHT.js.map} +1 -1
  133. package/dist/chunks/{ResetPasswordPage-Dqiahhnj.js → ResetPasswordPage-DqDD6VPR.js} +2 -2
  134. package/dist/chunks/{ResetPasswordPage-Dqiahhnj.js.map → ResetPasswordPage-DqDD6VPR.js.map} +1 -1
  135. package/dist/chunks/{ResetPasswordPage-CubPG3yv.js → ResetPasswordPage-Glu-aeqv.js} +2 -2
  136. package/dist/chunks/{ResetPasswordPage-CubPG3yv.js.map → ResetPasswordPage-Glu-aeqv.js.map} +1 -1
  137. package/dist/chunks/{ResolutionModal-Bg7XZmR1.js → ResolutionModal-CxjANAOP.js} +2 -2
  138. package/dist/chunks/{ResolutionModal-Bg7XZmR1.js.map → ResolutionModal-CxjANAOP.js.map} +1 -1
  139. package/dist/chunks/{ResolutionModal-DqRk_T0n.js → ResolutionModal-Duat18qV.js} +2 -2
  140. package/dist/chunks/{ResolutionModal-DqRk_T0n.js.map → ResolutionModal-Duat18qV.js.map} +1 -1
  141. package/dist/chunks/{RoleDetailPage-Blau6_4c.js → RoleDetailPage-BQffUSnt.js} +3 -3
  142. package/dist/chunks/{RoleDetailPage-Blau6_4c.js.map → RoleDetailPage-BQffUSnt.js.map} +1 -1
  143. package/dist/chunks/{RoleDetailPage-CiRVxxIP.js → RoleDetailPage-JTm5lD1_.js} +2 -2
  144. package/dist/chunks/{RoleDetailPage-CiRVxxIP.js.map → RoleDetailPage-JTm5lD1_.js.map} +1 -1
  145. package/dist/chunks/{RolesPage-Pm-RN3lP.js → RolesPage-B9rRzciI.js} +2 -2
  146. package/dist/chunks/{RolesPage-Pm-RN3lP.js.map → RolesPage-B9rRzciI.js.map} +1 -1
  147. package/dist/chunks/{RolesPage-Cb8joqdJ.js → RolesPage-BN8_zMOC.js} +2 -2
  148. package/dist/chunks/{RolesPage-Cb8joqdJ.js.map → RolesPage-BN8_zMOC.js.map} +1 -1
  149. package/dist/chunks/{SlaConfigPage-B86McKM6.js → SlaConfigPage-B7kZNig4.js} +2 -2
  150. package/dist/chunks/{SlaConfigPage-B86McKM6.js.map → SlaConfigPage-B7kZNig4.js.map} +1 -1
  151. package/dist/chunks/{SlaConfigPage-BY7gvYU6.js → SlaConfigPage-okvZfA_K.js} +2 -2
  152. package/dist/chunks/{SlaConfigPage-BY7gvYU6.js.map → SlaConfigPage-okvZfA_K.js.map} +1 -1
  153. package/dist/chunks/{SupportPermissionsPage-BYxcLMSd.js → SupportPermissionsPage-DGAPqJbl.js} +2 -2
  154. package/dist/chunks/{SupportPermissionsPage-BYxcLMSd.js.map → SupportPermissionsPage-DGAPqJbl.js.map} +1 -1
  155. package/dist/chunks/{SupportPermissionsPage-MXqXNJIZ.js → SupportPermissionsPage-Dg_wLOme.js} +2 -2
  156. package/dist/chunks/{SupportPermissionsPage-MXqXNJIZ.js.map → SupportPermissionsPage-Dg_wLOme.js.map} +1 -1
  157. package/dist/chunks/{TemplatesPage-BDguJ401.js → TemplatesPage-DT9fhlAU.js} +2 -2
  158. package/dist/chunks/{TemplatesPage-BDguJ401.js.map → TemplatesPage-DT9fhlAU.js.map} +1 -1
  159. package/dist/chunks/{TemplatesPage-DdnGgioU.js → TemplatesPage-DiEk538p.js} +2 -2
  160. package/dist/chunks/{TemplatesPage-DdnGgioU.js.map → TemplatesPage-DiEk538p.js.map} +1 -1
  161. package/dist/chunks/{TenantCard-ffwWsgFQ.js → TenantCard-BbSYk9_Z.js} +2 -2
  162. package/dist/chunks/{TenantCard-ffwWsgFQ.js.map → TenantCard-BbSYk9_Z.js.map} +1 -1
  163. package/dist/chunks/{TenantCard-CUjb6og9.js → TenantCard-CEkiKxcZ.js} +2 -2
  164. package/dist/chunks/{TenantCard-CUjb6og9.js.map → TenantCard-CEkiKxcZ.js.map} +1 -1
  165. package/dist/chunks/{TenantScopeSelector-Dz7i1I43.js → TenantScopeSelector-BWfYxvEa.js} +2 -2
  166. package/dist/chunks/{TenantScopeSelector-Dz7i1I43.js.map → TenantScopeSelector-BWfYxvEa.js.map} +1 -1
  167. package/dist/chunks/{TenantScopeSelector-Cym_Zyps.js → TenantScopeSelector-D-BKgQPV.js} +2 -2
  168. package/dist/chunks/{TenantScopeSelector-Cym_Zyps.js.map → TenantScopeSelector-D-BKgQPV.js.map} +1 -1
  169. package/dist/chunks/{TicketDetailPage-GOh9GX7E.js → TicketDetailPage-C1mNS9Up.js} +2 -2
  170. package/dist/chunks/{TicketDetailPage-GOh9GX7E.js.map → TicketDetailPage-C1mNS9Up.js.map} +1 -1
  171. package/dist/chunks/{TicketDetailPage-Du8WMyqf.js → TicketDetailPage-ieVDRh42.js} +2 -2
  172. package/dist/chunks/{TicketDetailPage-Du8WMyqf.js.map → TicketDetailPage-ieVDRh42.js.map} +1 -1
  173. package/dist/chunks/{TicketsPage-Bqd6moQy.js → TicketsPage-CnuWsnIW.js} +2 -2
  174. package/dist/chunks/{TicketsPage-Bqd6moQy.js.map → TicketsPage-CnuWsnIW.js.map} +1 -1
  175. package/dist/chunks/{TicketsPage-WdU4Bb7M.js → TicketsPage-jjyY15_D.js} +2 -2
  176. package/dist/chunks/{TicketsPage-WdU4Bb7M.js.map → TicketsPage-jjyY15_D.js.map} +1 -1
  177. package/dist/chunks/{UserCreateTicketPage-Cm1emgwR.js → UserCreateTicketPage-B8Tvf-ag.js} +2 -2
  178. package/dist/chunks/{UserCreateTicketPage-Cm1emgwR.js.map → UserCreateTicketPage-B8Tvf-ag.js.map} +1 -1
  179. package/dist/chunks/{UserCreateTicketPage-BPw-5Y_D.js → UserCreateTicketPage-DnOsDlfO.js} +2 -2
  180. package/dist/chunks/{UserCreateTicketPage-BPw-5Y_D.js.map → UserCreateTicketPage-DnOsDlfO.js.map} +1 -1
  181. package/dist/chunks/{UserDashboardPage-BP5WeXPS.js → UserDashboardPage-BrtkJ-NB.js} +2 -2
  182. package/dist/chunks/{UserDashboardPage-BP5WeXPS.js.map → UserDashboardPage-BrtkJ-NB.js.map} +1 -1
  183. package/dist/chunks/{UserDashboardPage-B53C8fUq.js → UserDashboardPage-KLB5CQP5.js} +2 -2
  184. package/dist/chunks/{UserDashboardPage-B53C8fUq.js.map → UserDashboardPage-KLB5CQP5.js.map} +1 -1
  185. package/dist/chunks/{UserDetailPage-B110bmGX.js → UserDetailPage-U7smBQoF.js} +5 -5
  186. package/dist/chunks/{UserDetailPage-B110bmGX.js.map → UserDetailPage-U7smBQoF.js.map} +1 -1
  187. package/dist/chunks/{UserDetailPage-CV2VCE46.js → UserDetailPage-_J6lcKAU.js} +2 -2
  188. package/dist/chunks/{UserDetailPage-CV2VCE46.js.map → UserDetailPage-_J6lcKAU.js.map} +1 -1
  189. package/dist/chunks/{UserTicketDetailPage-CCNJON1V.js → UserTicketDetailPage-CWoYQgH-.js} +2 -2
  190. package/dist/chunks/{UserTicketDetailPage-CCNJON1V.js.map → UserTicketDetailPage-CWoYQgH-.js.map} +1 -1
  191. package/dist/chunks/{UserTicketDetailPage-V0mLXrox.js → UserTicketDetailPage-DkufSlvZ.js} +2 -2
  192. package/dist/chunks/{UserTicketDetailPage-V0mLXrox.js.map → UserTicketDetailPage-DkufSlvZ.js.map} +1 -1
  193. package/dist/chunks/{UsersGroupsPage-CmdaU-z-.js → UsersGroupsPage-C38s2-Rq.js} +3 -3
  194. package/dist/chunks/{UsersGroupsPage-CmdaU-z-.js.map → UsersGroupsPage-C38s2-Rq.js.map} +1 -1
  195. package/dist/chunks/{UsersGroupsPage-BgfAMgEP.js → UsersGroupsPage-Dq3rAteo.js} +2 -2
  196. package/dist/chunks/{UsersGroupsPage-BgfAMgEP.js.map → UsersGroupsPage-Dq3rAteo.js.map} +1 -1
  197. package/dist/chunks/{UsersPage-Bg7033pp.js → UsersPage-B5C5KEUR.js} +2 -2
  198. package/dist/chunks/{UsersPage-Bg7033pp.js.map → UsersPage-B5C5KEUR.js.map} +1 -1
  199. package/dist/chunks/{UsersPage-TYAfwPY1.js → UsersPage-CXC9Hvq6.js} +2 -2
  200. package/dist/chunks/{UsersPage-TYAfwPY1.js.map → UsersPage-CXC9Hvq6.js.map} +1 -1
  201. package/dist/chunks/{accessRequestsApi-DZeDvzwv.js → accessRequestsApi-B-4TJ5_U.js} +2 -2
  202. package/dist/chunks/{accessRequestsApi-DZeDvzwv.js.map → accessRequestsApi-B-4TJ5_U.js.map} +1 -1
  203. package/dist/chunks/{accessRequestsApi-ZXFPCid2.js → accessRequestsApi-DZSfThpd.js} +2 -2
  204. package/dist/chunks/{accessRequestsApi-ZXFPCid2.js.map → accessRequestsApi-DZSfThpd.js.map} +1 -1
  205. package/dist/chunks/{aiApi-CsH8DXgs.js → aiApi-B20Teu2v.js} +2 -2
  206. package/dist/chunks/{aiApi-CsH8DXgs.js.map → aiApi-B20Teu2v.js.map} +1 -1
  207. package/dist/chunks/{aiApi-CVPzFTXa.js → aiApi-DMGz-RPM.js} +2 -2
  208. package/dist/chunks/{aiApi-CVPzFTXa.js.map → aiApi-DMGz-RPM.js.map} +1 -1
  209. package/dist/chunks/{applicationAnalyticsApi-B8AhFYLr.js → applicationAnalyticsApi-Bwa75Fzd.js} +2 -2
  210. package/dist/chunks/{applicationAnalyticsApi-B8AhFYLr.js.map → applicationAnalyticsApi-Bwa75Fzd.js.map} +1 -1
  211. package/dist/chunks/{applicationAnalyticsApi-Ce_1qOk-.js → applicationAnalyticsApi-CLBqRPfN.js} +2 -2
  212. package/dist/chunks/{applicationAnalyticsApi-Ce_1qOk-.js.map → applicationAnalyticsApi-CLBqRPfN.js.map} +1 -1
  213. package/dist/chunks/{groupsApi-BgCk2fsp.js → groupsApi-QzXI-5xu.js} +2 -2
  214. package/dist/chunks/{groupsApi-BgCk2fsp.js.map → groupsApi-QzXI-5xu.js.map} +1 -1
  215. package/dist/chunks/{groupsApi-BIbG665N.js → groupsApi-hB9kSWEd.js} +2 -2
  216. package/dist/chunks/{groupsApi-BIbG665N.js.map → groupsApi-hB9kSWEd.js.map} +1 -1
  217. package/dist/chunks/{index-Cb3LotuT.js → index--NGcBYUu.js} +3 -3
  218. package/dist/chunks/{index-Cb3LotuT.js.map → index--NGcBYUu.js.map} +1 -1
  219. package/dist/chunks/{index-C33zcyF4.js → index--aPwOFjF.js} +2 -2
  220. package/dist/chunks/{index-C33zcyF4.js.map → index--aPwOFjF.js.map} +1 -1
  221. package/dist/chunks/{index-sMr9qND_.js → index-0VrOtwP0.js} +2 -2
  222. package/dist/chunks/{index-sMr9qND_.js.map → index-0VrOtwP0.js.map} +1 -1
  223. package/dist/chunks/{index-DDKetfKq.js → index-37U271aw.js} +2 -2
  224. package/dist/chunks/{index-DDKetfKq.js.map → index-37U271aw.js.map} +1 -1
  225. package/dist/chunks/{index-B9fS7ir6.js → index-B7qZTuQ-.js} +2 -2
  226. package/dist/chunks/{index-B9fS7ir6.js.map → index-B7qZTuQ-.js.map} +1 -1
  227. package/dist/chunks/{index-CdjBY7L8.js → index-Bedzmqr-.js} +2 -2
  228. package/dist/chunks/{index-CdjBY7L8.js.map → index-Bedzmqr-.js.map} +1 -1
  229. package/dist/chunks/{index-CHG_O1fS.js → index-Betxo5g5.js} +2 -2
  230. package/dist/chunks/{index-CHG_O1fS.js.map → index-Betxo5g5.js.map} +1 -1
  231. package/dist/chunks/{index-jiGu-H8x.js → index-BmaJz475.js} +2 -2
  232. package/dist/chunks/{index-jiGu-H8x.js.map → index-BmaJz475.js.map} +1 -1
  233. package/dist/chunks/{index-C53JoVNk.js → index-Buhqag3v.js} +2 -2
  234. package/dist/chunks/{index-C53JoVNk.js.map → index-Buhqag3v.js.map} +1 -1
  235. package/dist/chunks/{index-DO0Rw7hX.js → index-C3VxlfKq.js} +2 -2
  236. package/dist/chunks/{index-DO0Rw7hX.js.map → index-C3VxlfKq.js.map} +1 -1
  237. package/dist/chunks/{index-CSQ60fpG.js → index-CgpRo8Oe.js} +2 -2
  238. package/dist/chunks/{index-CSQ60fpG.js.map → index-CgpRo8Oe.js.map} +1 -1
  239. package/dist/chunks/{index-B-e-ELsf.js → index-DOY0w8Iu.js} +8 -8
  240. package/dist/chunks/{index-B-e-ELsf.js.map → index-DOY0w8Iu.js.map} +1 -1
  241. package/dist/chunks/{index-D2REDIRX.js → index-DwuvIOrQ.js} +2 -2
  242. package/dist/chunks/{index-D2REDIRX.js.map → index-DwuvIOrQ.js.map} +1 -1
  243. package/dist/chunks/{index-DCcl7Qof.js → index-DzedSLdI.js} +2 -2
  244. package/dist/chunks/{index-DCcl7Qof.js.map → index-DzedSLdI.js.map} +1 -1
  245. package/dist/chunks/{index-2wUhd9Lu.js → index-IgLVXPg8.js} +10 -10
  246. package/dist/chunks/index-IgLVXPg8.js.map +1 -0
  247. package/dist/chunks/{index-CwSaRXXg.js → index-lpIzhufD.js} +1916 -1900
  248. package/dist/chunks/index-lpIzhufD.js.map +1 -0
  249. package/dist/chunks/{index-Cuwn2q-f.js → index-mLUKwbGl.js} +4 -4
  250. package/dist/chunks/{index-Cuwn2q-f.js.map → index-mLUKwbGl.js.map} +1 -1
  251. package/dist/chunks/{index-B0mk2tNY.js → index-tO6MMIFB.js} +2 -2
  252. package/dist/chunks/{index-B0mk2tNY.js.map → index-tO6MMIFB.js.map} +1 -1
  253. package/dist/chunks/{tenantIconMap-BpNANQ5s.js → tenantIconMap-BQD9byc8.js} +2 -2
  254. package/dist/chunks/{tenantIconMap-BpNANQ5s.js.map → tenantIconMap-BQD9byc8.js.map} +1 -1
  255. package/dist/chunks/{tenantIconMap-DtdUgvJO.js → tenantIconMap-CTMuSt18.js} +2 -2
  256. package/dist/chunks/{tenantIconMap-DtdUgvJO.js.map → tenantIconMap-CTMuSt18.js.map} +1 -1
  257. package/dist/chunks/{ticketingApi-Bu4rKwLl.js → ticketingApi-BNIdox5t.js} +2 -2
  258. package/dist/chunks/{ticketingApi-Bu4rKwLl.js.map → ticketingApi-BNIdox5t.js.map} +1 -1
  259. package/dist/chunks/{ticketingApi-r4Avm9iS.js → ticketingApi-J0vC_t7r.js} +2 -2
  260. package/dist/chunks/{ticketingApi-r4Avm9iS.js.map → ticketingApi-J0vC_t7r.js.map} +1 -1
  261. package/dist/chunks/{useAccessRequests-B9bF4swg.js → useAccessRequests-DCNNLnxk.js} +3 -3
  262. package/dist/chunks/{useAccessRequests-B9bF4swg.js.map → useAccessRequests-DCNNLnxk.js.map} +1 -1
  263. package/dist/chunks/{useAccessRequests-JyPUX3Om.js → useAccessRequests-DT7X4FAK.js} +2 -2
  264. package/dist/chunks/{useAccessRequests-JyPUX3Om.js.map → useAccessRequests-DT7X4FAK.js.map} +1 -1
  265. package/dist/chunks/{useUserAccessRequests-DjPQenC2.js → useUserAccessRequests-BYbmG4c7.js} +2 -2
  266. package/dist/chunks/{useUserAccessRequests-DjPQenC2.js.map → useUserAccessRequests-BYbmG4c7.js.map} +1 -1
  267. package/dist/chunks/{useUserAccessRequests-BB6FHW14.js → useUserAccessRequests-CylKxRN6.js} +2 -2
  268. package/dist/chunks/{useUserAccessRequests-BB6FHW14.js.map → useUserAccessRequests-CylKxRN6.js.map} +1 -1
  269. package/dist/hooks/useCollapsibleState.d.ts +5 -2
  270. package/dist/hooks/useCollapsibleState.d.ts.map +1 -1
  271. package/dist/i18n/config.d.ts +1 -0
  272. package/dist/i18n/config.d.ts.map +1 -1
  273. package/dist/main.d.ts +0 -1
  274. package/dist/main.d.ts.map +1 -1
  275. package/dist/smartstack.cjs +1 -1
  276. package/dist/smartstack.js +1 -1
  277. package/dist/utils/permissions.d.ts.map +1 -1
  278. package/package.json +1 -1
  279. package/dist/chunks/index-2wUhd9Lu.js.map +0 -1
  280. package/dist/chunks/index-CwSaRXXg.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-IgLVXPg8.js","sources":["../../src/types/theme.ts","../../src/config/themePresets.ts","../../src/types/errors.ts","../../src/services/logging/logService.ts","../../src/services/api/apiClient.ts","../../src/services/api/userApi.ts","../../src/services/api/adminApi/_shared.ts","../../src/services/api/adminApi/users.ts","../../src/services/api/adminApi/roles.ts","../../src/services/api/adminApi/applications.ts","../../src/services/api/adminApi/dashboard.ts","../../src/services/api/adminApi/settings.ts","../../src/services/api/adminApi/tenants.ts","../../src/services/api/adminApi/externalApps.ts","../../src/services/api/adminApi/index.ts","../../src/i18n/config.ts","../../src/contexts/ThemeContext.tsx","../../src/services/api/configApi.ts","../../src/contexts/FeatureConfigContext.tsx","../../src/utils/permissions.ts","../../src/contexts/AuthContext.tsx","../../src/utils/licenseUtils.ts","../../src/contexts/LicenseContext.tsx","../../src/contexts/TenantContext.tsx","../../src/contexts/FavoritesContext.tsx","../../src/contexts/SidebarContext.tsx","../../src/constants/routes.ts","../../src/contexts/NavigationContext.tsx","../../src/contexts/SignalRContext.tsx","../../src/services/api/authApi.ts","../../src/hooks/useSessionMonitor.ts","../../src/components/session/SessionWarningModal.tsx","../../src/components/session/SessionProvider.tsx","../../src/components/ThemeSync.tsx","../../src/components/errors/ErrorFallback.tsx","../../src/components/errors/GlobalErrorBoundary.tsx","../../src/extensions/SlotFill.tsx","../../src/extensions/ExtensionContext.tsx","../../src/extensions/PageRegistry.tsx","../../src/extensions/componentRegistry.generated.ts","../../src/provider/SmartStackProvider.tsx","../../src/contexts/DocPanelContext.tsx","../../src/services/support/browserInfoService.ts","../../src/hooks/useClientContext.ts","../../src/hooks/useCollapsibleState.ts","../../src/hooks/usePageTracking.ts","../../src/hooks/usePermissionMatrix.ts","../../src/hooks/useSignalR.ts","../../src/hooks/usePermissionSync.ts","../../src/hooks/useTicketSignalR.ts","../../src/hooks/useRouteConfig.ts","../../src/hooks/useEntityPermissions.ts","../../src/hooks/useUserPermissions.ts","../../src/components/ui/ThemeSwitcher.tsx","../../src/components/ui/LanguageSwitcher.tsx","../../src/utils/tenantUrl.ts","../../src/components/ui/AvatarMenu.tsx","../../src/components/ui/AppSwitcher.tsx","../../src/hooks/useTenantFavorites.ts","../../src/components/ui/TenantSelector.tsx","../../src/utils/notificationTenant.ts","../../src/services/api/supportApi.ts","../../src/hooks/useNotifications.ts","../../src/components/notifications/NotificationBell.tsx","../../src/components/ui/Tooltip.tsx","../../src/services/support/errorContextService.ts","../../src/services/support/ticketDraftService.ts","../../src/components/tickets/CreateTicketButton.tsx","../../src/components/layout/AppHeader.tsx","../../src/components/layout/ModuleSidebar.tsx","../../src/components/ui/DocPanel.tsx","../../src/components/layout/ResizableMainContent.tsx","../../src/components/ui/DocEdgeButton.tsx","../../src/components/license/LicenseAlertBanner.tsx","../../src/components/license/LicenseContentBanner.tsx","../../src/layouts/AppLayout.tsx","../../src/layouts/AuthLayout.tsx","../../src/components/layout/Header.tsx","../../src/components/layout/Footer.tsx","../../src/components/docs/DocsSearch.tsx","../../src/layouts/DocsLayout.tsx","../../src/layouts/PublicLayout.tsx","../../src/components/ui/Breadcrumb.tsx","../../src/components/ui/DataTable.tsx","../../src/components/ui/DataView.tsx","../../src/components/ui/EntityCard.tsx","../../src/components/ui/KanbanBoard.tsx","../../src/components/ui/PageHeader.tsx","../../src/components/ui/PermissionMatrix.tsx","../../src/components/ui/ThemeCustomizer.tsx","../../src/components/ui/UnderDevelopment.tsx","../../src/components/layout/AdminSidebar.tsx","../../src/components/layout/UserSidebar.tsx","../../src/components/errors/RouteErrorBoundary.tsx","../../src/components/license/FeatureGate.tsx","../../src/components/license/LicenseActivationDialog.tsx","../../src/components/license/LicenseStatusBadge.tsx","../../src/components/errors/AccessDenied.tsx","../../src/components/errors/NoActiveWorkspace.tsx","../../src/components/ui/PageLoader.tsx","../../src/components/routing/ProtectedRoute.tsx","../../src/components/routing/TenantRouteWrapper.tsx","../../src/components/routing/TenantNavigate.tsx","../../src/components/license/LicenseBlockOverlay.tsx","../../src/components/license/LicenseGuard.tsx","../../src/components/license/FeatureRouteGuard.tsx","../../src/components/tracking/PageTracker.tsx","../../src/services/api/tenantAnalyticsApi.ts","../../src/hooks/useTenantTracking.ts","../../src/components/tracking/TenantTracker.tsx","../../src/components/routing/DocRoutes.tsx","../../src/config/outletSections.ts","../../src/pages/HomePage.tsx","../../src/utils/deviceDetection.ts","../../src/utils/ipDetection.ts","../../src/hooks/useLoginForm.ts","../../src/pages/LoginPage.tsx","../../src/pages/NotFoundPage.tsx","../../src/components/routing/DynamicRouter.tsx","../../src/components/favorites/FavoritesModal.tsx","../../src/components/favorites/FavoritesBar.tsx","../../src/components/platform/support/ActivityTimeline.tsx","../../src/components/platform/support/SlaBadge.tsx","../../src/components/platform/support/TemplatePicker.tsx","../../src/components/platform/support/TicketConversation.tsx","../../src/components/platform/support/FilePreviewModal.tsx","../../src/components/platform/support/TicketSidebar.tsx","../../src/components/platform/support/TicketWorkflowHorizontal.tsx","../../src/components/platform/support/TicketSolutionPanel.tsx","../../src/services/api/aiRoutingApi.ts","../../src/components/platform/support/AiClassificationBadge.tsx","../../src/hooks/useAiOperation.ts","../../src/components/platform/support/AiRoutingSuggestionPanel.tsx","../../src/components/platform/support/AiTicketSummary.tsx","../../src/services/api/ticketApi.ts","../../src/services/api/assignmentApi.ts","../../src/hooks/useTicketingProvider.ts","../../src/constants/supportTheme.ts","../../src/components/platform/support/TicketDetailView.tsx","../../src/components/platform/support/TicketsListView.tsx"],"sourcesContent":["export type ThemeMode = 'light' | 'dark';\r\n\r\nexport type BorderRadiusSize = 'none' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge';\r\n\r\nexport type ItemPaletteKey = 'neutral' | 'ocean' | 'forest' | 'sunset' | 'lavender' | 'rose';\r\n\r\nexport interface ItemPaletteColors {\r\n light: string; // Lightest shade (background)\r\n medium: string; // Medium shade (accent)\r\n dark: string; // Darkest shade (text accent)\r\n border: string; // Border color\r\n}\r\n\r\nexport interface BorderRadiusConfig {\r\n card: BorderRadiusSize;\r\n button: BorderRadiusSize;\r\n badge: BorderRadiusSize;\r\n input: BorderRadiusSize;\r\n modal: BorderRadiusSize;\r\n menuItem: BorderRadiusSize;\r\n}\r\n\r\nexport interface ColorShades {\r\n 50: string;\r\n 100: string;\r\n 200: string;\r\n 300: string;\r\n 400: string;\r\n 500: string;\r\n 600: string;\r\n 700: string;\r\n 800: string;\r\n 900: string;\r\n 950: string;\r\n}\r\n\r\nexport interface StatusColor {\r\n bg: string;\r\n text: string;\r\n border: string;\r\n dot: string;\r\n}\r\n\r\nexport interface BackgroundColors {\r\n app: string;\r\n primary: string;\r\n secondary: string;\r\n tertiary: string;\r\n card: string;\r\n hover: string;\r\n active: string;\r\n}\r\n\r\nexport interface TextColors {\r\n primary: string;\r\n secondary: string;\r\n tertiary: string;\r\n muted: string;\r\n inverse: string;\r\n}\r\n\r\nexport interface BorderColors {\r\n default: string;\r\n subtle: string;\r\n strong: string;\r\n}\r\n\r\nexport interface StatusColors {\r\n success: StatusColor;\r\n warning: StatusColor;\r\n error: StatusColor;\r\n info: StatusColor;\r\n}\r\n\r\nexport interface AccentColors {\r\n bg: string;\r\n text: string;\r\n border: string;\r\n}\r\n\r\nexport interface ColorPalette {\r\n name: string;\r\n primary: ColorShades;\r\n accent: ColorShades;\r\n backgrounds: BackgroundColors;\r\n text: TextColors;\r\n border: BorderColors;\r\n status: StatusColors;\r\n accentTransparent: AccentColors;\r\n}\r\n\r\nexport interface ThemeConfig {\r\n mode: ThemeMode;\r\n borderRadius: BorderRadiusConfig;\r\n colors: {\r\n light: ColorPalette;\r\n dark: ColorPalette;\r\n };\r\n accentColorKey: string;\r\n itemPaletteKey: ItemPaletteKey; // Gradient palette for card/item backgrounds\r\n}\r\n\r\nexport interface ThemePreset {\r\n id: string;\r\n name: string;\r\n description: string;\r\n icon: string;\r\n config: Partial<ThemeConfig>;\r\n}\r\n\r\nexport const BORDER_RADIUS_VALUES: Record<BorderRadiusSize, string> = {\r\n none: '0px',\r\n small: '4px',\r\n medium: '8px',\r\n large: '12px',\r\n xlarge: '16px',\r\n xxlarge: '24px',\r\n};\r\n\r\nexport const BORDER_RADIUS_LABELS: Record<BorderRadiusSize, string> = {\r\n none: 'Carré',\r\n small: 'Léger',\r\n medium: 'Moyen',\r\n large: 'Arrondi',\r\n xlarge: 'Très arrondi',\r\n xxlarge: 'Pill',\r\n};\r\n\r\nexport const ITEM_PALETTE_LABELS: Record<ItemPaletteKey, string> = {\r\n neutral: 'Neutre',\r\n ocean: 'Océan',\r\n forest: 'Forêt',\r\n sunset: 'Coucher de soleil',\r\n lavender: 'Lavande',\r\n rose: 'Rose',\r\n};\r\n","import type { ThemeConfig, ThemePreset, ColorPalette, ColorShades, BorderRadiusConfig, ItemPaletteKey, ItemPaletteColors } from '@/types/theme';\r\n\r\nexport const ACCENT_COLORS: Record<string, { name: string; shades: ColorShades }> = {\r\n // === NEUTRAL COLORS ===\r\n slate: {\r\n name: 'Ardoise',\r\n shades: {\r\n 50: '#f8fafc', 100: '#f1f5f9', 200: '#e2e8f0', 300: '#cbd5e1', 400: '#94a3b8',\r\n 500: '#64748b', 600: '#475569', 700: '#334155', 800: '#1e293b', 900: '#0f172a', 950: '#020617'\r\n }\r\n },\r\n gray: {\r\n name: 'Gris',\r\n shades: {\r\n 50: '#f9fafb', 100: '#f3f4f6', 200: '#e5e7eb', 300: '#d1d5db', 400: '#9ca3af',\r\n 500: '#6b7280', 600: '#4b5563', 700: '#374151', 800: '#1f2937', 900: '#111827', 950: '#030712'\r\n }\r\n },\r\n zinc: {\r\n name: 'Zinc',\r\n shades: {\r\n 50: '#fafafa', 100: '#f4f4f5', 200: '#e4e4e7', 300: '#d4d4d8', 400: '#a1a1aa',\r\n 500: '#71717a', 600: '#52525b', 700: '#3f3f46', 800: '#27272a', 900: '#18181b', 950: '#09090b'\r\n }\r\n },\r\n stone: {\r\n name: 'Pierre',\r\n shades: {\r\n 50: '#fafaf9', 100: '#f5f5f4', 200: '#e7e5e4', 300: '#d6d3d1', 400: '#a8a29e',\r\n 500: '#78716c', 600: '#57534e', 700: '#44403c', 800: '#292524', 900: '#1c1917', 950: '#0c0a09'\r\n }\r\n },\r\n // === VIBRANT COLORS ===\r\n indigo: {\r\n name: 'Indigo',\r\n shades: {\r\n 50: '#eef2ff', 100: '#e0e7ff', 200: '#c7d2fe', 300: '#a5b4fc', 400: '#818cf8',\r\n 500: '#6366f1', 600: '#4f46e5', 700: '#4338ca', 800: '#3730a3', 900: '#312e81', 950: '#1e1b4b'\r\n }\r\n },\r\n violet: {\r\n name: 'Violet',\r\n shades: {\r\n 50: '#f5f3ff', 100: '#ede9fe', 200: '#ddd6fe', 300: '#c4b5fd', 400: '#a78bfa',\r\n 500: '#8b5cf6', 600: '#7c3aed', 700: '#6d28d9', 800: '#5b21b6', 900: '#4c1d95', 950: '#2e1065'\r\n }\r\n },\r\n blue: {\r\n name: 'Bleu',\r\n shades: {\r\n 50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa',\r\n 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a', 950: '#172554'\r\n }\r\n },\r\n cyan: {\r\n name: 'Cyan',\r\n shades: {\r\n 50: '#ecfeff', 100: '#cffafe', 200: '#a5f3fc', 300: '#67e8f9', 400: '#22d3ee',\r\n 500: '#06b6d4', 600: '#0891b2', 700: '#0e7490', 800: '#155e75', 900: '#164e63', 950: '#083344'\r\n }\r\n },\r\n teal: {\r\n name: 'Teal',\r\n shades: {\r\n 50: '#f0fdfa', 100: '#ccfbf1', 200: '#99f6e4', 300: '#5eead4', 400: '#2dd4bf',\r\n 500: '#14b8a6', 600: '#0d9488', 700: '#0f766e', 800: '#115e59', 900: '#134e4a', 950: '#042f2e'\r\n }\r\n },\r\n emerald: {\r\n name: 'Émeraude',\r\n shades: {\r\n 50: '#ecfdf5', 100: '#d1fae5', 200: '#a7f3d0', 300: '#6ee7b7', 400: '#34d399',\r\n 500: '#10b981', 600: '#059669', 700: '#047857', 800: '#065f46', 900: '#064e3b', 950: '#022c22'\r\n }\r\n },\r\n amber: {\r\n name: 'Ambre',\r\n shades: {\r\n 50: '#fffbeb', 100: '#fef3c7', 200: '#fde68a', 300: '#fcd34d', 400: '#fbbf24',\r\n 500: '#f59e0b', 600: '#d97706', 700: '#b45309', 800: '#92400e', 900: '#78350f', 950: '#451a03'\r\n }\r\n },\r\n orange: {\r\n name: 'Orange',\r\n shades: {\r\n 50: '#fff7ed', 100: '#ffedd5', 200: '#fed7aa', 300: '#fdba74', 400: '#fb923c',\r\n 500: '#f97316', 600: '#ea580c', 700: '#c2410c', 800: '#9a3412', 900: '#7c2d12', 950: '#431407'\r\n }\r\n },\r\n rose: {\r\n name: 'Rose',\r\n shades: {\r\n 50: '#fff1f2', 100: '#ffe4e6', 200: '#fecdd3', 300: '#fda4af', 400: '#fb7185',\r\n 500: '#f43f5e', 600: '#e11d48', 700: '#be123c', 800: '#9f1239', 900: '#881337', 950: '#4c0519'\r\n }\r\n },\r\n pink: {\r\n name: 'Pink',\r\n shades: {\r\n 50: '#fdf2f8', 100: '#fce7f3', 200: '#fbcfe8', 300: '#f9a8d4', 400: '#f472b6',\r\n 500: '#ec4899', 600: '#db2777', 700: '#be185d', 800: '#9d174d', 900: '#831843', 950: '#500724'\r\n }\r\n },\r\n};\r\n\r\nexport const DEFAULT_BORDER_RADIUS: BorderRadiusConfig = {\r\n card: 'large',\r\n button: 'medium',\r\n badge: 'medium',\r\n input: 'medium',\r\n modal: 'xlarge',\r\n menuItem: 'medium',\r\n};\r\n\r\n// Item palettes for card/item backgrounds - each has 3 colors for gradient (light → medium → dark)\r\n// Dark mode: subtle tinted backgrounds that work well on dark surfaces\r\nexport const ITEM_PALETTES: Record<ItemPaletteKey, { name: string; colors: { light: ItemPaletteColors; dark: ItemPaletteColors }; icon: string }> = {\r\n neutral: {\r\n name: 'Neutre',\r\n icon: '⚪',\r\n colors: {\r\n light: { light: '#ffffff', medium: '#f4f4f5', dark: '#e4e4e7', border: '#d4d4d8' },\r\n dark: { light: '#1c1c1f', medium: '#27272a', dark: '#3f3f46', border: '#52525b' },\r\n },\r\n },\r\n ocean: {\r\n name: 'Océan',\r\n icon: '🌊',\r\n colors: {\r\n light: { light: '#e0f2fe', medium: '#bae6fd', dark: '#7dd3fc', border: '#93c5fd' },\r\n dark: { light: '#141c24', medium: '#1a2836', dark: '#2a4a5e', border: '#3a5a70' },\r\n },\r\n },\r\n forest: {\r\n name: 'Forêt',\r\n icon: '🌲',\r\n colors: {\r\n light: { light: '#d1fae5', medium: '#a7f3d0', dark: '#6ee7b7', border: '#86efac' },\r\n dark: { light: '#121d18', medium: '#182a22', dark: '#2a4a3a', border: '#3a5a4a' },\r\n },\r\n },\r\n sunset: {\r\n name: 'Coucher de soleil',\r\n icon: '🌅',\r\n colors: {\r\n light: { light: '#fef3c7', medium: '#fde68a', dark: '#fbbf24', border: '#fcd34d' },\r\n dark: { light: '#1c1810', medium: '#2a2418', dark: '#4a3a24', border: '#5a4a34' },\r\n },\r\n },\r\n lavender: {\r\n name: 'Lavande',\r\n icon: '💜',\r\n colors: {\r\n light: { light: '#ede9fe', medium: '#ddd6fe', dark: '#c4b5fd', border: '#c4b5fd' },\r\n dark: { light: '#1a1820', medium: '#24222e', dark: '#3a3650', border: '#4a4660' },\r\n },\r\n },\r\n rose: {\r\n name: 'Rose',\r\n icon: '🌸',\r\n colors: {\r\n light: { light: '#fce7f3', medium: '#fbcfe8', dark: '#f9a8d4', border: '#f9a8d4' },\r\n dark: { light: '#1c1418', medium: '#2a1e24', dark: '#4a3040', border: '#5a4050' },\r\n },\r\n },\r\n};\r\n\r\nexport const DEFAULT_ITEM_PALETTE: ItemPaletteKey = 'neutral';\r\n\r\nexport const createLightPalette = (accentKey: string): ColorPalette => {\r\n const accent = ACCENT_COLORS[accentKey]?.shades || ACCENT_COLORS.indigo.shades;\r\n return {\r\n name: 'Clair',\r\n primary: ACCENT_COLORS.indigo.shades,\r\n accent,\r\n backgrounds: {\r\n app: '#fafafa',\r\n primary: '#ffffff',\r\n secondary: '#f4f4f5',\r\n tertiary: '#e4e4e7',\r\n card: '#ffffff',\r\n hover: '#f4f4f5',\r\n active: '#e4e4e7',\r\n },\r\n text: {\r\n primary: '#18181b',\r\n secondary: '#52525b',\r\n tertiary: '#71717a',\r\n muted: '#a1a1aa',\r\n inverse: '#fafafa',\r\n },\r\n border: {\r\n default: '#e4e4e7',\r\n subtle: '#f4f4f5',\r\n strong: '#d4d4d8',\r\n },\r\n status: {\r\n success: { bg: '#f0fdfa', text: '#0f766e', border: '#99f6e4', dot: '#14b8a6' },\r\n warning: { bg: '#fffbeb', text: '#b45309', border: '#fde68a', dot: '#f59e0b' },\r\n error: { bg: '#fff1f2', text: '#be123c', border: '#fecdd3', dot: '#f43f5e' },\r\n info: { bg: '#f0f9ff', text: '#0369a1', border: '#bae6fd', dot: '#0ea5e9' },\r\n },\r\n accentTransparent: {\r\n bg: `${accent[50]}`,\r\n text: accent[700],\r\n border: accent[200],\r\n },\r\n };\r\n};\r\n\r\nexport const createDarkPalette = (accentKey: string): ColorPalette => {\r\n const accent = ACCENT_COLORS[accentKey]?.shades || ACCENT_COLORS.indigo.shades;\r\n return {\r\n name: 'Sombre',\r\n primary: ACCENT_COLORS.indigo.shades,\r\n accent,\r\n backgrounds: {\r\n app: '#161618',\r\n primary: '#1c1c1f',\r\n secondary: '#232326',\r\n tertiary: '#2a2a2e',\r\n card: '#1c1c1f',\r\n hover: '#27272a',\r\n active: '#3f3f46',\r\n },\r\n text: {\r\n primary: '#fafafa',\r\n secondary: '#d4d4d8',\r\n tertiary: '#a1a1aa',\r\n muted: '#71717a',\r\n inverse: '#18181b',\r\n },\r\n border: {\r\n default: '#3f3f46',\r\n subtle: '#27272a',\r\n strong: '#52525b',\r\n },\r\n status: {\r\n success: { bg: 'rgba(20, 184, 166, 0.15)', text: '#5eead4', border: 'rgba(20, 184, 166, 0.3)', dot: '#14b8a6' },\r\n warning: { bg: 'rgba(245, 158, 11, 0.15)', text: '#fbbf24', border: 'rgba(245, 158, 11, 0.3)', dot: '#f59e0b' },\r\n error: { bg: 'rgba(244, 63, 94, 0.15)', text: '#fb7185', border: 'rgba(244, 63, 94, 0.3)', dot: '#f43f5e' },\r\n info: { bg: 'rgba(14, 165, 233, 0.15)', text: '#7dd3fc', border: 'rgba(14, 165, 233, 0.3)', dot: '#0ea5e9' },\r\n },\r\n accentTransparent: {\r\n bg: `rgba(${hexToRgb(accent[500])}, 0.15)`,\r\n text: accent[300],\r\n border: `rgba(${hexToRgb(accent[500])}, 0.3)`,\r\n },\r\n };\r\n};\r\n\r\nfunction hexToRgb(hex: string): string {\r\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\r\n if (!result) return '99, 102, 241';\r\n return `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}`;\r\n}\r\n\r\nexport const DEFAULT_THEME_CONFIG: ThemeConfig = {\r\n mode: 'dark',\r\n borderRadius: DEFAULT_BORDER_RADIUS,\r\n colors: {\r\n light: createLightPalette('indigo'),\r\n dark: createDarkPalette('indigo'),\r\n },\r\n accentColorKey: 'indigo',\r\n itemPaletteKey: DEFAULT_ITEM_PALETTE,\r\n};\r\n\r\nexport const THEME_PRESETS: ThemePreset[] = [\r\n {\r\n id: 'default',\r\n name: 'Par défaut',\r\n description: 'Style SmartStack classique avec coins arrondis',\r\n icon: '✨',\r\n config: {\r\n borderRadius: { card: 'large', button: 'medium', badge: 'medium', input: 'medium', modal: 'xlarge', menuItem: 'medium' },\r\n accentColorKey: 'indigo',\r\n itemPaletteKey: 'neutral',\r\n },\r\n },\r\n {\r\n id: 'minimal',\r\n name: 'Minimal',\r\n description: 'Style épuré et professionnel',\r\n icon: '⚪',\r\n config: {\r\n borderRadius: { card: 'medium', button: 'small', badge: 'small', input: 'small', modal: 'medium', menuItem: 'small' },\r\n accentColorKey: 'slate',\r\n itemPaletteKey: 'neutral',\r\n },\r\n },\r\n {\r\n id: 'corporate',\r\n name: 'Corporate',\r\n description: 'Style sobre pour environnement professionnel',\r\n icon: '🏢',\r\n config: {\r\n borderRadius: { card: 'small', button: 'small', badge: 'medium', input: 'small', modal: 'medium', menuItem: 'small' },\r\n accentColorKey: 'gray',\r\n itemPaletteKey: 'neutral',\r\n },\r\n },\r\n {\r\n id: 'ocean',\r\n name: 'Océan',\r\n description: 'Teintes bleues apaisantes',\r\n icon: '🌊',\r\n config: {\r\n borderRadius: { card: 'large', button: 'medium', badge: 'medium', input: 'medium', modal: 'xlarge', menuItem: 'medium' },\r\n accentColorKey: 'cyan',\r\n itemPaletteKey: 'ocean',\r\n },\r\n },\r\n {\r\n id: 'forest',\r\n name: 'Forêt',\r\n description: 'Tons verts naturels',\r\n icon: '🌲',\r\n config: {\r\n borderRadius: { card: 'medium', button: 'medium', badge: 'medium', input: 'medium', modal: 'large', menuItem: 'medium' },\r\n accentColorKey: 'emerald',\r\n itemPaletteKey: 'forest',\r\n },\r\n },\r\n {\r\n id: 'sunset',\r\n name: 'Coucher de soleil',\r\n description: 'Couleurs chaudes et accueillantes',\r\n icon: '🌅',\r\n config: {\r\n borderRadius: { card: 'large', button: 'large', badge: 'xxlarge', input: 'medium', modal: 'xlarge', menuItem: 'medium' },\r\n accentColorKey: 'orange',\r\n itemPaletteKey: 'sunset',\r\n },\r\n },\r\n {\r\n id: 'lavender',\r\n name: 'Lavande',\r\n description: 'Tons violets élégants',\r\n icon: '💜',\r\n config: {\r\n borderRadius: { card: 'xlarge', button: 'large', badge: 'xlarge', input: 'large', modal: 'xlarge', menuItem: 'large' },\r\n accentColorKey: 'violet',\r\n itemPaletteKey: 'lavender',\r\n },\r\n },\r\n {\r\n id: 'rose',\r\n name: 'Rose',\r\n description: 'Teintes roses douces',\r\n icon: '🌸',\r\n config: {\r\n borderRadius: { card: 'xxlarge', button: 'xlarge', badge: 'xxlarge', input: 'large', modal: 'xxlarge', menuItem: 'large' },\r\n accentColorKey: 'pink',\r\n itemPaletteKey: 'rose',\r\n },\r\n },\r\n];\r\n\r\nexport const THEME_STORAGE_KEY = 'smartstack-theme-config';\r\n","export interface ApiError {\r\n message: string;\r\n details?: Record<string, string[]> | string[];\r\n statusCode?: number;\r\n}\r\n\r\nexport interface ClientErrorLog {\r\n message: string;\r\n stack?: string;\r\n url?: string;\r\n component?: string;\r\n level?: 'Error' | 'Warning' | 'Critical';\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\nexport interface ClientEventLog {\r\n event: string;\r\n category: string;\r\n data?: Record<string, unknown>;\r\n}\r\n\r\nexport class AppError extends Error {\r\n statusCode?: number;\r\n details?: Record<string, string[]> | string[];\r\n\r\n constructor(\r\n message: string,\r\n statusCode?: number,\r\n details?: Record<string, string[]> | string[]\r\n ) {\r\n super(message);\r\n this.name = 'AppError';\r\n this.statusCode = statusCode;\r\n this.details = details;\r\n }\r\n}\r\n\r\nexport class NetworkError extends Error {\r\n constructor(message: string = 'Network error occurred') {\r\n super(message);\r\n this.name = 'NetworkError';\r\n }\r\n}\r\n\r\nexport type LoginErrorCode =\r\n | 'INVALID_CREDENTIALS'\r\n | 'ACCOUNT_DISABLED'\r\n | 'ACCOUNT_LOCKED'\r\n | 'EXTERNAL_AUTH_REQUIRED'\r\n | 'INVALID_PASSWORD';\r\n\r\nexport class AuthenticationError extends Error {\r\n code?: LoginErrorCode;\r\n\r\n constructor(message: string = 'Authentication required', code?: LoginErrorCode) {\r\n super(message);\r\n this.name = 'AuthenticationError';\r\n this.code = code;\r\n }\r\n}\r\n\r\nexport class ForbiddenError extends Error {\r\n constructor(message: string = 'Access denied') {\r\n super(message);\r\n this.name = 'ForbiddenError';\r\n }\r\n}\r\n","import type { ClientErrorLog, ClientEventLog } from '@/types/errors';\r\n\r\nconst MAX_RECENT_ERRORS = 10;\r\n\r\ntype LogQueueItem =\r\n | { type: 'error'; data: ClientErrorLog }\r\n | { type: 'event'; data: ClientEventLog };\r\n\r\nclass LogService {\r\n private queue: LogQueueItem[] = [];\r\n private recentErrors: Array<ClientErrorLog & { timestamp: Date }> = [];\r\n private isProcessing = false;\r\n private batchSize = 10;\r\n private batchDelay = 2000;\r\n\r\n async logError(error: Error | string, component?: string, metadata?: Record<string, unknown>): Promise<void> {\r\n const baseMessage = typeof error === 'string' ? error : error.message;\r\n\r\n // Build a more descriptive message for API errors that includes the endpoint\r\n let displayMessage = baseMessage;\r\n if (metadata?.url) {\r\n const apiUrl = String(metadata.url);\r\n const status = metadata.status ? ` [${metadata.status}]` : '';\r\n displayMessage = `${baseMessage}${status} - ${apiUrl}`;\r\n }\r\n\r\n const errorLog: ClientErrorLog = {\r\n message: displayMessage,\r\n stack: typeof error === 'string' ? undefined : error.stack,\r\n url: window.location.href,\r\n component,\r\n level: 'Error',\r\n metadata\r\n };\r\n\r\n this.storeRecentError(errorLog);\r\n this.queue.push({ type: 'error', data: errorLog });\r\n this.processQueue();\r\n\r\n if (import.meta.env.DEV) {\r\n console.error('[LogService]', errorLog);\r\n }\r\n }\r\n\r\n async logCritical(error: Error | string, component?: string, metadata?: Record<string, unknown>): Promise<string | null> {\r\n const baseMessage = typeof error === 'string' ? error : error.message;\r\n\r\n // Build a more descriptive message for API errors that includes the endpoint\r\n let displayMessage = baseMessage;\r\n if (metadata?.url) {\r\n const apiUrl = String(metadata.url);\r\n const status = metadata.status ? ` [${metadata.status}]` : '';\r\n displayMessage = `${baseMessage}${status} - ${apiUrl}`;\r\n }\r\n\r\n const errorLog: ClientErrorLog = {\r\n message: displayMessage,\r\n stack: typeof error === 'string' ? undefined : error.stack,\r\n url: window.location.href,\r\n component,\r\n level: 'Critical',\r\n metadata\r\n };\r\n\r\n this.storeRecentError(errorLog);\r\n const ticketNumber = await this.sendErrorWithResponse(errorLog);\r\n\r\n if (import.meta.env.DEV) {\r\n console.error('[LogService:CRITICAL]', errorLog, ticketNumber ? `Ticket: ${ticketNumber}` : '');\r\n }\r\n\r\n return ticketNumber;\r\n }\r\n\r\n getRecentErrors(): Array<ClientErrorLog & { timestamp: Date }> {\r\n return [...this.recentErrors];\r\n }\r\n\r\n clearRecentErrors(): void {\r\n this.recentErrors = [];\r\n }\r\n\r\n private storeRecentError(errorLog: ClientErrorLog): void {\r\n this.recentErrors.unshift({ ...errorLog, timestamp: new Date() });\r\n if (this.recentErrors.length > MAX_RECENT_ERRORS) {\r\n this.recentErrors.pop();\r\n }\r\n }\r\n\r\n async logEvent(event: string, category: string, data?: Record<string, unknown>): Promise<void> {\r\n const eventLog: ClientEventLog = {\r\n event,\r\n category,\r\n data\r\n };\r\n\r\n this.queue.push({ type: 'event', data: eventLog });\r\n this.processQueue();\r\n\r\n if (import.meta.env.DEV) {\r\n console.log('[LogService:Event]', eventLog);\r\n }\r\n }\r\n\r\n private async processQueue(): Promise<void> {\r\n if (this.isProcessing || this.queue.length === 0) return;\r\n\r\n this.isProcessing = true;\r\n\r\n await new Promise(resolve => setTimeout(resolve, this.batchDelay));\r\n\r\n const batch = this.queue.splice(0, this.batchSize);\r\n\r\n for (const item of batch) {\r\n try {\r\n if (item.type === 'error') {\r\n await this.sendError(item.data);\r\n } else {\r\n await this.sendEvent(item.data);\r\n }\r\n } catch {\r\n if (import.meta.env.DEV) {\r\n console.warn('[LogService] Failed to send log:', item);\r\n }\r\n }\r\n }\r\n\r\n this.isProcessing = false;\r\n\r\n if (this.queue.length > 0) {\r\n this.processQueue();\r\n }\r\n }\r\n\r\n private async sendError(error: ClientErrorLog): Promise<void> {\r\n const token = localStorage.getItem('token');\r\n\r\n await fetch(`/api/logs/client-error`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...(token && { Authorization: `Bearer ${token}` })\r\n },\r\n body: JSON.stringify(error)\r\n });\r\n }\r\n\r\n private async sendErrorWithResponse(error: ClientErrorLog): Promise<string | null> {\r\n const token = localStorage.getItem('token');\r\n\r\n try {\r\n const response = await fetch(`/api/logs/client-error`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...(token && { Authorization: `Bearer ${token}` })\r\n },\r\n body: JSON.stringify(error)\r\n });\r\n\r\n if (response.ok && response.status === 200) {\r\n const data = await response.json();\r\n return data.ticketNumber || null;\r\n }\r\n return null;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n private async sendEvent(event: ClientEventLog): Promise<void> {\r\n const token = localStorage.getItem('token');\r\n\r\n await fetch(`/api/logs/client-event`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...(token && { Authorization: `Bearer ${token}` })\r\n },\r\n body: JSON.stringify(event)\r\n });\r\n }\r\n}\r\n\r\nexport const logService = new LogService();\r\n\r\nwindow.onerror = (message, source, lineno, colno, error) => {\r\n logService.logError(\r\n error || String(message),\r\n 'window.onerror',\r\n { source, lineno, colno }\r\n );\r\n};\r\n\r\nwindow.onunhandledrejection = (event) => {\r\n logService.logError(\r\n event.reason instanceof Error ? event.reason : String(event.reason),\r\n 'unhandledrejection'\r\n );\r\n};\r\n","import axios, { AxiosError, type AxiosInstance, type InternalAxiosRequestConfig } from 'axios';\r\nimport { AppError, AuthenticationError, ForbiddenError, NetworkError, type LoginErrorCode } from '@/types/errors';\r\nimport { logService } from '@/services/logging/logService';\r\n\r\n// Use relative URLs to leverage Vite proxy in development\r\n// In production, configure reverse proxy (nginx, etc.) to route /api to backend\r\nconst apiClient: AxiosInstance = axios.create({\r\n timeout: 30000,\r\n headers: {\r\n 'Content-Type': 'application/json'\r\n }\r\n});\r\n\r\napiClient.interceptors.request.use(\r\n (config: InternalAxiosRequestConfig) => {\r\n const token = localStorage.getItem('token');\r\n if (token && config.headers) {\r\n config.headers.Authorization = `Bearer ${token}`;\r\n }\r\n\r\n const correlationId = crypto.randomUUID();\r\n if (config.headers) {\r\n config.headers['X-Correlation-ID'] = correlationId;\r\n }\r\n\r\n // Set Accept-Language header based on user's locale preference\r\n const locale = localStorage.getItem('i18nextLng') || navigator.language || 'fr';\r\n if (config.headers) {\r\n config.headers['Accept-Language'] = locale;\r\n }\r\n\r\n // Set X-Tenant-Slug header for multi-tenant context (only if not explicitly set)\r\n // This allows createAdminApi(slug) to override the default localStorage value\r\n const tenantSlug = localStorage.getItem('currentTenantSlug');\r\n if (tenantSlug && config.headers && !config.headers['X-Tenant-Slug']) {\r\n config.headers['X-Tenant-Slug'] = tenantSlug;\r\n }\r\n\r\n return config;\r\n },\r\n (error: AxiosError) => {\r\n logService.logError(error, 'apiClient.request');\r\n return Promise.reject(error);\r\n }\r\n);\r\n\r\napiClient.interceptors.response.use(\r\n (response) => response,\r\n async (error: AxiosError<{ message?: string; code?: string; details?: Record<string, string[]>; detail?: string; title?: string; errors?: Record<string, string[]> }>) => {\r\n const correlationId = error.config?.headers?.['X-Correlation-ID'];\r\n\r\n // Ignore canceled requests (AbortController.abort() or component unmount)\r\n // These are not real network errors and should not be logged\r\n if (axios.isCancel(error) || error.code === 'ERR_CANCELED') {\r\n return Promise.reject(error);\r\n }\r\n\r\n if (!error.response) {\r\n const networkError = new NetworkError('Impossible de se connecter au serveur. Vérifiez votre connexion internet.');\r\n logService.logError(networkError, 'apiClient.response', { correlationId });\r\n return Promise.reject(networkError);\r\n }\r\n\r\n const { status, data } = error.response;\r\n // Support both custom format (message) and ASP.NET Core ProblemDetails (detail/title)\r\n const message = data?.message || data?.detail || data?.title || error.message;\r\n const code = data?.code;\r\n\r\n switch (status) {\r\n case 401: {\r\n const authError = new AuthenticationError(message, code as LoginErrorCode | undefined);\r\n logService.logError(authError, 'apiClient.response', {\r\n correlationId,\r\n url: error.config?.url,\r\n code\r\n });\r\n\r\n const requestUrl = error.config?.url || '';\r\n const nonCriticalEndpoints = [\r\n '/api/user/preferences',\r\n '/api/auth/session-info',\r\n '/api/auth/refresh',\r\n '/api/navigation/menu',\r\n '/api/navigation/favorites',\r\n '/api/navigation/contexts',\r\n '/api/onboarding/status',\r\n '/api/administration/tenants/tenant-analytics/track/',\r\n ];\r\n const isNonCritical = nonCriticalEndpoints.some(ep => requestUrl.startsWith(ep));\r\n\r\n if (!window.location.pathname.includes('/login') && !isNonCritical) {\r\n localStorage.removeItem('token');\r\n localStorage.removeItem('refreshToken');\r\n localStorage.removeItem('user');\r\n\r\n const isSessionExpired = code === 'SESSION_EXPIRED';\r\n const redirectUrl = isSessionExpired\r\n ? '/login?reason=session_expired'\r\n : '/login';\r\n\r\n window.location.href = redirectUrl;\r\n }\r\n return Promise.reject(authError);\r\n }\r\n\r\n case 403: {\r\n const forbiddenError = new ForbiddenError(message);\r\n\r\n // Best-effort endpoints: 403 is expected, log as warning instead of critical\r\n const bestEffortEndpoints = [\r\n '/api/administration/tenants/tenant-analytics/track/',\r\n '/api/support/notifications'\r\n ];\r\n const forbiddenUrl = error.config?.url || '';\r\n const isBestEffort = bestEffortEndpoints.some(ep => forbiddenUrl.startsWith(ep));\r\n\r\n if (!isBestEffort) {\r\n logService.logError(forbiddenError, 'apiClient.response', {\r\n correlationId,\r\n url: error.config?.url,\r\n method: error.config?.method\r\n });\r\n }\r\n return Promise.reject(forbiddenError);\r\n }\r\n\r\n case 404:\r\n return Promise.reject(new AppError(message || 'Resource not found', status));\r\n\r\n case 400:\r\n return Promise.reject(new AppError(message || 'Invalid request', status, data?.details || data?.errors));\r\n\r\n case 500:\r\n case 502:\r\n case 503:\r\n case 504: {\r\n const serverError = new AppError(message || 'Server error', status);\r\n logService.logCritical(serverError, 'apiClient.response', {\r\n correlationId,\r\n url: error.config?.url,\r\n status\r\n });\r\n return Promise.reject(serverError);\r\n }\r\n\r\n default: {\r\n const appError = new AppError(message, status, data?.details);\r\n logService.logError(appError, 'apiClient.response', { correlationId, status });\r\n return Promise.reject(appError);\r\n }\r\n }\r\n }\r\n);\r\n\r\nexport { apiClient };\r\n\r\nexport const api = {\r\n get: <T>(url: string, config?: Parameters<typeof apiClient.get>[1]) =>\r\n apiClient.get<T>(url, config).then(res => res.data),\r\n\r\n post: <T>(url: string, data?: unknown, config?: Parameters<typeof apiClient.post>[2]) =>\r\n apiClient.post<T>(url, data, config).then(res => res.data),\r\n\r\n put: <T>(url: string, data?: unknown, config?: Parameters<typeof apiClient.put>[2]) =>\r\n apiClient.put<T>(url, data, config).then(res => res.data),\r\n\r\n patch: <T>(url: string, data?: unknown, config?: Parameters<typeof apiClient.patch>[2]) =>\r\n apiClient.patch<T>(url, data, config).then(res => res.data),\r\n\r\n delete: <T>(url: string, config?: Parameters<typeof apiClient.delete>[1]) =>\r\n apiClient.delete<T>(url, config).then(res => res.data)\r\n};\r\n","import { api } from './apiClient';\r\n\r\nexport interface BorderRadiusDto {\r\n card: string;\r\n button: string;\r\n badge: string;\r\n input: string;\r\n modal: string;\r\n menuItem: string;\r\n}\r\n\r\nexport interface UserPreferencesDto {\r\n theme: string;\r\n language: string;\r\n timeZone: string | null;\r\n emailNotifications: boolean;\r\n pushNotifications: boolean;\r\n accentColorKey: string;\r\n borderRadius: BorderRadiusDto;\r\n itemPaletteKey?: string;\r\n}\r\n\r\nexport interface UpdateThemeRequest {\r\n mode: string;\r\n accentColorKey: string;\r\n borderRadius: BorderRadiusDto;\r\n itemPaletteKey?: string;\r\n /**\r\n * Target tenant ID for saving preferences.\r\n * - undefined/null = save to global preferences (applies to all tenants)\r\n * - string = save to specific tenant override\r\n */\r\n targetTenantId?: string | null;\r\n}\r\n\r\nexport interface UpdateLanguageRequest {\r\n language: string;\r\n}\r\n\r\nexport interface UpdateNotificationsRequest {\r\n emailNotifications: boolean;\r\n pushNotifications: boolean;\r\n}\r\n\r\nexport interface DailySessionStatsDto {\r\n date: string;\r\n loginCount: number;\r\n avgDuration: number;\r\n}\r\n\r\nexport interface SessionAnalyticsDto {\r\n dailyStats: DailySessionStatsDto[];\r\n totalSessions: number;\r\n avgSessionDuration: number;\r\n}\r\n\r\nexport interface LoginErrorDto {\r\n loginAt: string;\r\n failureReason: string;\r\n ipAddress: string | null;\r\n country: string | null;\r\n city: string | null;\r\n}\r\n\r\nexport interface CurrentSessionDto {\r\n loginAt: string;\r\n durationMinutes: number;\r\n ipAddress: string | null;\r\n userAgent: string | null;\r\n country: string | null;\r\n city: string | null;\r\n}\r\n\r\nexport interface BrowserCountDto {\r\n browser: string;\r\n count: number;\r\n}\r\n\r\nexport interface CountryCountDto {\r\n country: string;\r\n count: number;\r\n}\r\n\r\nexport interface DeviceStatsDto {\r\n byBrowser: BrowserCountDto[];\r\n byCountry: CountryCountDto[];\r\n mostUsedDevice: string;\r\n}\r\n\r\nexport interface DayOfWeekStatsDto {\r\n dayOfWeek: number;\r\n dayName: string;\r\n sessionCount: number;\r\n totalDuration: number;\r\n}\r\n\r\nexport interface WeeklyStatsDto {\r\n dayStats: DayOfWeekStatsDto[];\r\n avgDurationPerDay: number;\r\n}\r\n\r\nexport interface HourStatsDto {\r\n hour: number;\r\n sessionCount: number;\r\n totalDuration: number;\r\n}\r\n\r\nexport interface HourlyStatsDto {\r\n hourStats: HourStatsDto[];\r\n firstActiveHour: number;\r\n lastActiveHour: number;\r\n peakHour: number;\r\n avgDurationPerHour: number;\r\n}\r\n\r\nexport interface AppUsageItemDto {\r\n applicationId: string;\r\n applicationName: string;\r\n applicationIcon: string | null;\r\n accessCount: number;\r\n totalDurationSeconds: number;\r\n percentage: number;\r\n}\r\n\r\nexport interface ApplicationUsageDto {\r\n applications: AppUsageItemDto[];\r\n totalAccess: number;\r\n}\r\n\r\n/**\r\n * Aggregated DTO containing all dashboard data in a single response.\r\n * Reduces 7 API calls to 1 for initial page load.\r\n */\r\nexport interface DashboardAllDto {\r\n sessionAnalytics: SessionAnalyticsDto;\r\n loginErrors: LoginErrorDto[];\r\n currentSession: CurrentSessionDto | null;\r\n deviceStats: DeviceStatsDto;\r\n weeklyStats: WeeklyStatsDto;\r\n hourlyStats: HourlyStatsDto;\r\n applicationUsage: ApplicationUsageDto;\r\n}\r\n\r\nexport const userApi = {\r\n preferences: {\r\n get: () =>\r\n api.get<UserPreferencesDto>('/api/user/preferences'),\r\n\r\n updateTheme: (data: UpdateThemeRequest) =>\r\n api.put<UserPreferencesDto>('/api/user/preferences/theme', data),\r\n\r\n updateLanguage: (data: UpdateLanguageRequest) =>\r\n api.put<UserPreferencesDto>('/api/user/preferences/language', data),\r\n\r\n updateNotifications: (data: UpdateNotificationsRequest) =>\r\n api.put<UserPreferencesDto>('/api/user/preferences/notifications', data),\r\n },\r\n\r\n dashboard: {\r\n /**\r\n * Get all dashboard data in a single call (optimized for initial load).\r\n * Reduces 7 API calls to 1.\r\n */\r\n getAll: (period: 7 | 30 | 90 = 30, errorLimit: number = 10) =>\r\n api.get<DashboardAllDto>(`/api/user/dashboard/all?period=${period}&errorLimit=${errorLimit}`),\r\n\r\n getSessionAnalytics: (period: 7 | 30 | 90 = 30) =>\r\n api.get<SessionAnalyticsDto>(`/api/user/dashboard/session-analytics?period=${period}`),\r\n\r\n getLoginErrors: (limit: number = 10) =>\r\n api.get<LoginErrorDto[]>(`/api/user/dashboard/login-errors?limit=${limit}`),\r\n\r\n getCurrentSession: () =>\r\n api.get<CurrentSessionDto>('/api/user/dashboard/current-session'),\r\n\r\n getDeviceStats: () =>\r\n api.get<DeviceStatsDto>('/api/user/dashboard/device-stats'),\r\n\r\n getWeeklyStats: (period: 7 | 30 | 90 = 30) =>\r\n api.get<WeeklyStatsDto>(`/api/user/dashboard/weekly-stats?period=${period}`),\r\n\r\n getHourlyStats: (period: 7 | 30 | 90 = 30) =>\r\n api.get<HourlyStatsDto>(`/api/user/dashboard/hourly-stats?period=${period}`),\r\n\r\n getApplicationUsage: (period: 7 | 30 | 90 = 30) =>\r\n api.get<ApplicationUsageDto>(`/api/user/dashboard/application-usage?period=${period}`),\r\n },\r\n};\r\n","import { api, apiClient } from '../apiClient';\r\nimport type { PaginatedResult } from '../../../types/pagination';\r\n\r\n/**\r\n * ApiContext type matching the shape of the `api` object from apiClient.\r\n * Used by all domain factory functions as their parameter type.\r\n */\r\nexport type ApiContext = typeof api;\r\n\r\n/**\r\n * Creates API methods with a specific tenant context.\r\n * Used when Super Admins view tenant-specific pages without \"switching\" to that tenant.\r\n */\r\nconst withTenantContext = (tenantSlug?: string): ApiContext => ({\r\n get: <T>(url: string, config?: Parameters<typeof apiClient.get>[1]) =>\r\n apiClient.get<T>(url, {\r\n ...config,\r\n headers: tenantSlug ? { ...config?.headers, 'X-Tenant-Slug': tenantSlug } : config?.headers,\r\n }).then(res => res.data),\r\n\r\n post: <T>(url: string, data?: unknown, config?: Parameters<typeof apiClient.post>[2]) =>\r\n apiClient.post<T>(url, data, {\r\n ...config,\r\n headers: tenantSlug ? { ...config?.headers, 'X-Tenant-Slug': tenantSlug } : config?.headers,\r\n }).then(res => res.data),\r\n\r\n put: <T>(url: string, data?: unknown, config?: Parameters<typeof apiClient.put>[2]) =>\r\n apiClient.put<T>(url, data, {\r\n ...config,\r\n headers: tenantSlug ? { ...config?.headers, 'X-Tenant-Slug': tenantSlug } : config?.headers,\r\n }).then(res => res.data),\r\n\r\n patch: <T>(url: string, data?: unknown, config?: Parameters<typeof apiClient.patch>[2]) =>\r\n apiClient.patch<T>(url, data, {\r\n ...config,\r\n headers: tenantSlug ? { ...config?.headers, 'X-Tenant-Slug': tenantSlug } : config?.headers,\r\n }).then(res => res.data),\r\n\r\n delete: <T>(url: string, config?: Parameters<typeof apiClient.delete>[1]) =>\r\n apiClient.delete<T>(url, {\r\n ...config,\r\n headers: tenantSlug ? { ...config?.headers, 'X-Tenant-Slug': tenantSlug } : config?.headers,\r\n }).then(res => res.data),\r\n});\r\n\r\nexport { api, apiClient, withTenantContext };\r\nexport type { PaginatedResult };\r\n","import type { ApiContext, PaginatedResult } from './_shared';\r\nimport type { RoleDto, RoleDetailDto } from './roles';\r\nimport type {\r\n UserTenantDto,\r\n UserGroupDto,\r\n UserConsolidatedRoleDto,\r\n UserApplicationAccessDto,\r\n} from './crossCutting';\r\n\r\n// User Types\r\nexport interface UserListDto {\r\n id: string;\r\n email: string;\r\n firstName: string;\r\n lastName: string;\r\n fullName: string;\r\n isActive: boolean;\r\n userType: string;\r\n roles: string[];\r\n lastLoginAt: string | null;\r\n createdAt: string;\r\n // Entra ID sync indicator\r\n entraObjectId: string | null;\r\n lastEntraSyncAt: string | null;\r\n // User purpose from mailboxSettings (user, shared, room, equipment)\r\n entraUserPurpose: string | null;\r\n // Tenant membership source: Direct, Group, or Both\r\n membershipSource: 'Direct' | 'Group' | 'Both';\r\n // Primary group granting access (if via group)\r\n primaryGroupId: string | null;\r\n primaryGroupName: string | null;\r\n}\r\n\r\nexport interface UserDetailDto {\r\n id: string;\r\n email: string;\r\n firstName: string;\r\n lastName: string;\r\n fullName: string;\r\n isActive: boolean;\r\n userType: string;\r\n roles: RoleDto[];\r\n lastLoginAt: string | null;\r\n createdAt: string;\r\n updatedAt: string | null;\r\n isLocked: boolean;\r\n lockoutEnd: string | null;\r\n lastUnlockReason: number | null;\r\n lockedBy: string | null;\r\n lockedAt: string | null;\r\n isTemporarilyLocked: boolean;\r\n temporaryLockoutEnd: string | null;\r\n // Entra ID integration\r\n entraObjectId: string | null;\r\n authProvider: string | null;\r\n lastEntraSyncAt: string | null;\r\n entraUserPurpose: string | null;\r\n // Reference data (IDs and Names)\r\n departmentId: string | null;\r\n departmentName: string | null;\r\n jobTitleId: string | null;\r\n jobTitleName: string | null;\r\n companyId: string | null;\r\n companyName: string | null;\r\n officeId: string | null;\r\n officeName: string | null;\r\n // Job Information\r\n employeeId: string | null;\r\n employeeType: string | null;\r\n employeeHireDate: string | null;\r\n employeeOrgData: string | null;\r\n managerId: string | null;\r\n managerName: string | null;\r\n sponsors: string | null;\r\n // Contact - Phone\r\n phoneNumber: string | null;\r\n mobilePhone: string | null;\r\n faxNumber: string | null;\r\n // Contact - Address\r\n streetAddress: string | null;\r\n city: string | null;\r\n state: string | null;\r\n postalCode: string | null;\r\n country: string | null;\r\n // Contact - Other\r\n otherEmails: string | null;\r\n proxyAddresses: string | null;\r\n imAddresses: string | null;\r\n mailNickname: string | null;\r\n // Account status\r\n emailConfirmed: boolean;\r\n hasCompletedOnboarding: boolean;\r\n mustChangePassword: boolean;\r\n}\r\n\r\n// User History Types\r\nexport interface UserHistoryDto {\r\n id: string;\r\n actionCode: number;\r\n actionName: string;\r\n fieldChanged: string | null;\r\n oldValue: string | null;\r\n newValue: string | null;\r\n changedByUserId: string | null;\r\n changedByUserName: string | null;\r\n createdAt: string;\r\n}\r\n\r\n// Section Update Request Types\r\nexport interface UpdateProfileRequest {\r\n firstName?: string;\r\n lastName?: string;\r\n}\r\n\r\nexport interface UpdateOrganizationRequest {\r\n departmentId?: string | null;\r\n departmentName?: string | null; // For creating new department\r\n jobTitleId?: string | null;\r\n jobTitleName?: string | null; // For creating new job title\r\n companyId?: string | null;\r\n companyName?: string | null; // For creating new company\r\n officeId?: string | null;\r\n officeName?: string | null; // For creating new office\r\n managerId?: string | null;\r\n}\r\n\r\nexport interface UpdateJobInfoRequest {\r\n employeeId?: string | null;\r\n employeeType?: string | null;\r\n employeeHireDate?: string | null;\r\n employeeOrgData?: string | null;\r\n}\r\n\r\nexport interface UpdatePhonesRequest {\r\n phoneNumber?: string | null;\r\n mobilePhone?: string | null;\r\n faxNumber?: string | null;\r\n}\r\n\r\nexport interface UpdateAddressRequest {\r\n streetAddress?: string | null;\r\n city?: string | null;\r\n state?: string | null;\r\n postalCode?: string | null;\r\n country?: string | null;\r\n}\r\n\r\nexport interface UpdateContactRequest {\r\n otherEmails?: string | null;\r\n proxyAddresses?: string | null;\r\n imAddresses?: string | null;\r\n mailNickname?: string | null;\r\n}\r\n\r\nexport interface ReferenceItemDto {\r\n id: string;\r\n code: string;\r\n name: string;\r\n}\r\n\r\n// References Management DTOs\r\nexport interface ReferenceListDto {\r\n id: string;\r\n code: string;\r\n name: string;\r\n description: string | null;\r\n isActive: boolean;\r\n isFromEntra: boolean;\r\n entraValue: string | null;\r\n userCount: number;\r\n createdAt: string;\r\n tenantId: string | null;\r\n tenantName: string | null;\r\n}\r\n\r\nexport interface OfficeListDto extends ReferenceListDto {\r\n address: string | null;\r\n city: string | null;\r\n country: string | null;\r\n}\r\n\r\nexport interface CreateReferenceRequest {\r\n code: string;\r\n name: string;\r\n description?: string;\r\n}\r\n\r\nexport interface CreateOfficeRequest extends CreateReferenceRequest {\r\n address?: string;\r\n city?: string;\r\n country?: string;\r\n}\r\n\r\nexport interface UpdateReferenceRequest {\r\n name: string;\r\n description?: string;\r\n}\r\n\r\nexport interface UpdateOfficeRequest extends UpdateReferenceRequest {\r\n address?: string;\r\n city?: string;\r\n country?: string;\r\n}\r\n\r\nexport interface OrganizationReferencesDto {\r\n departments: ReferenceItemDto[];\r\n companies: ReferenceItemDto[];\r\n jobTitles: ReferenceItemDto[];\r\n offices: ReferenceItemDto[];\r\n managers: ReferenceItemDto[];\r\n}\r\n\r\nexport const UnlockReason = {\r\n PasswordResetMalfunction: 1,\r\n FalsePositive: 2,\r\n UrgentBusinessNeed: 3,\r\n TechnicalSystemError: 4,\r\n SupportTicketVerified: 5\r\n} as const;\r\n\r\nexport type UnlockReason = typeof UnlockReason[keyof typeof UnlockReason];\r\n\r\nexport interface UnlockAccountRequest {\r\n reason: UnlockReason;\r\n mustChangePassword: boolean;\r\n}\r\n\r\nexport interface CreateUserRequest {\r\n email: string;\r\n password: string;\r\n firstName: string;\r\n lastName: string;\r\n roleIds?: string[];\r\n}\r\n\r\nexport interface UpdateUserRequest {\r\n email?: string;\r\n firstName: string;\r\n lastName: string;\r\n roleIds?: string[];\r\n}\r\n\r\nconst _createUsersApi = (apiContext: ApiContext) => ({\r\n getAll: (params?: {\r\n page?: number;\r\n pageSize?: number;\r\n search?: string;\r\n isActive?: boolean;\r\n entraOnly?: boolean;\r\n roleId?: string;\r\n userType?: string;\r\n sortBy?: string;\r\n sortDesc?: boolean;\r\n globalScope?: boolean;\r\n }) =>\r\n apiContext.get<PaginatedResult<UserListDto>>('/api/administration/users', { params }),\r\n\r\n getById: (id: string) =>\r\n apiContext.get<UserDetailDto>(`/api/administration/users/${id}`),\r\n\r\n create: (data: CreateUserRequest) =>\r\n apiContext.post<UserDetailDto>('/api/administration/users', data),\r\n\r\n update: (id: string, data: UpdateUserRequest) =>\r\n apiContext.put<UserDetailDto>(`/api/administration/users/${id}`, data),\r\n\r\n sendPasswordResetLink: (id: string) =>\r\n apiContext.post<{ message: string }>(`/api/administration/users/${id}/send-password-reset`),\r\n\r\n resetPasswordDirect: (id: string, temporaryPassword?: string) =>\r\n apiContext.post<{ message: string; temporaryPassword: string; mustChangePassword: boolean }>(\r\n `/api/administration/users/${id}/reset-password-direct`,\r\n temporaryPassword ? { temporaryPassword } : {}\r\n ),\r\n\r\n activate: (id: string) =>\r\n apiContext.patch(`/api/administration/users/${id}/activate`),\r\n\r\n deactivate: (id: string) =>\r\n apiContext.patch(`/api/administration/users/${id}/deactivate`),\r\n\r\n unlock: (id: string, data: UnlockAccountRequest) =>\r\n apiContext.post<{ message: string; mustChangePassword: boolean }>(`/api/administration/users/${id}/unlock`, data),\r\n\r\n clearFailedAttempts: (id: string) =>\r\n apiContext.post<{ message: string; clearedCount: number }>(`/api/administration/users/${id}/clear-failed-attempts`),\r\n\r\n delete: (id: string) =>\r\n apiContext.delete(`/api/administration/users/${id}`),\r\n\r\n // Sessions\r\n getSessions: (id: string) =>\r\n apiContext.get<UserSessionsDto>(`/api/administration/users/${id}/sessions`),\r\n\r\n endAllSessions: (id: string) =>\r\n apiContext.post(`/api/administration/users/${id}/sessions/end-all`),\r\n\r\n endSession: (userId: string, sessionId: string) =>\r\n apiContext.delete(`/api/administration/users/${userId}/sessions/${sessionId}`),\r\n\r\n // Activity\r\n getActivity: (id: string) =>\r\n apiContext.get<UserActivityDto>(`/api/administration/users/${id}/activity`),\r\n\r\n // Custom Role for user-specific permissions\r\n getCustomRole: (id: string) =>\r\n apiContext.get<RoleDetailDto | null>(`/api/administration/users/${id}/custom-role`),\r\n\r\n createCustomRole: (id: string) =>\r\n apiContext.post<RoleDetailDto>(`/api/administration/users/${id}/custom-role`),\r\n\r\n deleteCustomRole: (id: string) =>\r\n apiContext.delete(`/api/administration/users/${id}/custom-role`),\r\n\r\n // History\r\n getHistory: (id: string, limit: number = 50) =>\r\n apiContext.get<UserHistoryDto[]>(`/api/administration/users/${id}/history`, { params: { limit } }),\r\n\r\n restoreFromHistory: (userId: string, historyId: string) =>\r\n apiContext.post<{ message: string; field: string; restoredValue: string }>(\r\n `/api/administration/users/${userId}/history/${historyId}/restore`\r\n ),\r\n\r\n // Organization References\r\n getOrganizationReferences: () =>\r\n apiContext.get<OrganizationReferencesDto>('/api/administration/users/organization-references'),\r\n\r\n // References Management\r\n references: {\r\n getCompanies: (params?: { tenantId?: string; search?: string; isActive?: boolean; source?: 'entra' | 'manual' }) =>\r\n apiContext.get<ReferenceListDto[]>('/api/administration/users/references/companies', { params }),\r\n\r\n getDepartments: (params?: { tenantId?: string; search?: string; isActive?: boolean; source?: 'entra' | 'manual' }) =>\r\n apiContext.get<ReferenceListDto[]>('/api/administration/users/references/departments', { params }),\r\n\r\n getJobTitles: (params?: { tenantId?: string; search?: string; isActive?: boolean; source?: 'entra' | 'manual' }) =>\r\n apiContext.get<ReferenceListDto[]>('/api/administration/users/references/job-titles', { params }),\r\n\r\n getOffices: (params?: { tenantId?: string; search?: string; isActive?: boolean; source?: 'entra' | 'manual' }) =>\r\n apiContext.get<OfficeListDto[]>('/api/administration/users/references/offices', { params }),\r\n\r\n createCompany: (data: CreateReferenceRequest, tenantId?: string) =>\r\n apiContext.post<ReferenceListDto>(`/api/administration/users/references/companies${tenantId ? `?tenantId=${tenantId}` : ''}`, data),\r\n\r\n createDepartment: (data: CreateReferenceRequest, tenantId?: string) =>\r\n apiContext.post<ReferenceListDto>(`/api/administration/users/references/departments${tenantId ? `?tenantId=${tenantId}` : ''}`, data),\r\n\r\n createJobTitle: (data: CreateReferenceRequest, tenantId?: string) =>\r\n apiContext.post<ReferenceListDto>(`/api/administration/users/references/job-titles${tenantId ? `?tenantId=${tenantId}` : ''}`, data),\r\n\r\n createOffice: (data: CreateOfficeRequest, tenantId?: string) =>\r\n apiContext.post<OfficeListDto>(`/api/administration/users/references/offices${tenantId ? `?tenantId=${tenantId}` : ''}`, data),\r\n\r\n updateCompany: (id: string, data: UpdateReferenceRequest) =>\r\n apiContext.put(`/api/administration/users/references/companies/${id}`, data),\r\n\r\n updateDepartment: (id: string, data: UpdateReferenceRequest) =>\r\n apiContext.put(`/api/administration/users/references/departments/${id}`, data),\r\n\r\n updateJobTitle: (id: string, data: UpdateReferenceRequest) =>\r\n apiContext.put(`/api/administration/users/references/job-titles/${id}`, data),\r\n\r\n updateOffice: (id: string, data: UpdateOfficeRequest) =>\r\n apiContext.put(`/api/administration/users/references/offices/${id}`, data),\r\n\r\n activateCompany: (id: string) =>\r\n apiContext.patch(`/api/administration/users/references/companies/${id}/activate`),\r\n\r\n activateDepartment: (id: string) =>\r\n apiContext.patch(`/api/administration/users/references/departments/${id}/activate`),\r\n\r\n activateJobTitle: (id: string) =>\r\n apiContext.patch(`/api/administration/users/references/job-titles/${id}/activate`),\r\n\r\n activateOffice: (id: string) =>\r\n apiContext.patch(`/api/administration/users/references/offices/${id}/activate`),\r\n\r\n deactivateCompany: (id: string) =>\r\n apiContext.patch(`/api/administration/users/references/companies/${id}/deactivate`),\r\n\r\n deactivateDepartment: (id: string) =>\r\n apiContext.patch(`/api/administration/users/references/departments/${id}/deactivate`),\r\n\r\n deactivateJobTitle: (id: string) =>\r\n apiContext.patch(`/api/administration/users/references/job-titles/${id}/deactivate`),\r\n\r\n deactivateOffice: (id: string) =>\r\n apiContext.patch(`/api/administration/users/references/offices/${id}/deactivate`),\r\n\r\n deleteCompany: (id: string) =>\r\n apiContext.delete(`/api/administration/users/references/companies/${id}`),\r\n\r\n deleteDepartment: (id: string) =>\r\n apiContext.delete(`/api/administration/users/references/departments/${id}`),\r\n\r\n deleteJobTitle: (id: string) =>\r\n apiContext.delete(`/api/administration/users/references/job-titles/${id}`),\r\n\r\n deleteOffice: (id: string) =>\r\n apiContext.delete(`/api/administration/users/references/offices/${id}`),\r\n },\r\n\r\n // Section Updates\r\n updateProfile: (id: string, data: UpdateProfileRequest) =>\r\n apiContext.put(`/api/administration/users/${id}/profile`, data),\r\n\r\n updateOrganization: (id: string, data: UpdateOrganizationRequest) =>\r\n apiContext.put(`/api/administration/users/${id}/organization`, data),\r\n\r\n updateJobInfo: (id: string, data: UpdateJobInfoRequest) =>\r\n apiContext.put(`/api/administration/users/${id}/job-info`, data),\r\n\r\n updatePhones: (id: string, data: UpdatePhonesRequest) =>\r\n apiContext.put(`/api/administration/users/${id}/phones`, data),\r\n\r\n updateAddress: (id: string, data: UpdateAddressRequest) =>\r\n apiContext.put(`/api/administration/users/${id}/address`, data),\r\n\r\n updateContact: (id: string, data: UpdateContactRequest) =>\r\n apiContext.put(`/api/administration/users/${id}/contact`, data),\r\n\r\n // Tenants\r\n getTenants: (id: string) =>\r\n apiContext.get<UserTenantDto[]>(`/api/administration/users/${id}/tenants`),\r\n\r\n // Groups\r\n getGroups: (id: string) =>\r\n apiContext.get<UserGroupDto[]>(`/api/administration/users/${id}/groups`),\r\n\r\n // Consolidated Roles (for permission matrix view - works in both global and tenant-scoped modes)\r\n getConsolidatedRoles: (id: string, tenantId?: string) => {\r\n const url = tenantId\r\n ? `/api/administration/users/${id}/consolidated-roles?tenantId=${tenantId}`\r\n : `/api/administration/users/${id}/consolidated-roles`;\r\n return apiContext.get<UserConsolidatedRoleDto[]>(url);\r\n },\r\n\r\n // Application Access (shows which applications a user can access with permissions)\r\n getApplicationAccess: (id: string, tenantId?: string) => {\r\n const url = tenantId\r\n ? `/api/administration/users/${id}/application-access?tenantId=${tenantId}`\r\n : `/api/administration/users/${id}/application-access`;\r\n return apiContext.get<UserApplicationAccessDto[]>(url);\r\n }\r\n});\r\nexport type UsersApi = ReturnType<typeof _createUsersApi>;\r\nexport const createUsersApi: (apiContext: ApiContext) => UsersApi = _createUsersApi;\r\n\r\n\r\n// Re-export types used by the API methods from dashboard (session/activity)\r\n// These are defined in dashboard.ts but used by users API methods\r\nimport type { UserSessionsDto, UserActivityDto } from './dashboard';\r\nexport type { UserSessionsDto, UserActivityDto };\r\n","import type { ApiContext } from './_shared';\r\n\r\n// Role Types\r\nexport type RoleCategory = 'Global' | 'Admin' | 'Manager' | 'Contributor' | 'Viewer' | 'Custom';\r\n\r\nexport interface RoleDto {\r\n id: string;\r\n name: string;\r\n shortName: string;\r\n category: RoleCategory;\r\n description: string | null;\r\n}\r\n\r\nexport interface RoleListDto {\r\n id: string;\r\n name: string;\r\n shortName: string;\r\n code: string | null;\r\n category: RoleCategory;\r\n description: string | null;\r\n isSystem: boolean;\r\n usersCount: number;\r\n permissionsCount: number;\r\n createdAt: string;\r\n applicationId: string | null;\r\n applicationName: string | null;\r\n contextId: string | null;\r\n contextName: string | null;\r\n}\r\n\r\nexport interface RoleDetailDto {\r\n id: string;\r\n name: string;\r\n shortName: string;\r\n code: string | null;\r\n category: RoleCategory;\r\n description: string | null;\r\n isSystem: boolean;\r\n parentRoleId: string | null;\r\n users: RoleUserDto[];\r\n permissions: RolePermissionDto[];\r\n groups: RoleGroupDto[];\r\n createdAt: string;\r\n updatedAt: string | null;\r\n}\r\n\r\nexport interface RoleUserDto {\r\n id: string;\r\n email: string;\r\n fullName: string;\r\n}\r\n\r\nexport interface RolePermissionDto {\r\n id: string;\r\n path: string;\r\n description: string | null;\r\n}\r\n\r\nexport interface RoleGroupDto {\r\n id: string;\r\n name: string;\r\n description: string | null;\r\n membersCount: number;\r\n}\r\n\r\nexport interface CreateRoleRequest {\r\n name: string;\r\n shortName?: string;\r\n category?: RoleCategory;\r\n description?: string;\r\n parentRoleId?: string;\r\n permissionIds?: string[];\r\n}\r\n\r\nexport interface UpdateRoleRequest {\r\n name: string;\r\n shortName?: string;\r\n category?: RoleCategory;\r\n description?: string;\r\n parentRoleId?: string;\r\n permissionIds?: string[];\r\n}\r\n\r\nexport interface RoleTranslationDto {\r\n id: string;\r\n languageCode: string;\r\n name: string;\r\n shortName: string;\r\n description: string | null;\r\n}\r\n\r\nexport interface UpdateRoleTranslationRequest {\r\n name: string;\r\n shortName: string;\r\n description?: string;\r\n}\r\n\r\n// Permission Types\r\nexport interface PermissionListDto {\r\n id: string;\r\n path: string;\r\n level: string;\r\n action: string | null;\r\n isWildcard: boolean;\r\n description: string | null;\r\n rolesCount: number;\r\n}\r\n\r\nexport interface PermissionDetailDto {\r\n id: string;\r\n path: string;\r\n level: string;\r\n action: string | null;\r\n isWildcard: boolean;\r\n description: string | null;\r\n contextId: string | null;\r\n applicationId: string | null;\r\n moduleId: string | null;\r\n sectionId: string | null;\r\n resourceId: string | null;\r\n roles: PermissionRoleDto[];\r\n users: PermissionUserDto[];\r\n entraGroups: PermissionEntraGroupDto[];\r\n createdAt: string;\r\n}\r\n\r\nexport interface PermissionRoleDto {\r\n id: string;\r\n name: string;\r\n isSystem: boolean;\r\n}\r\n\r\nexport interface PermissionUserDto {\r\n id: string;\r\n email: string;\r\n firstName: string;\r\n lastName: string;\r\n roleNames: string[];\r\n}\r\n\r\nexport interface PermissionEntraGroupDto {\r\n id: string;\r\n displayName: string;\r\n isSecurityGroup: boolean;\r\n memberCount: number;\r\n roleNames: string[];\r\n}\r\n\r\nexport interface PermissionTreeDto {\r\n applications: PermissionApplicationNode[];\r\n}\r\n\r\nexport interface PermissionApplicationNode {\r\n id: string;\r\n code: string;\r\n label: string;\r\n permissionPath: string;\r\n modules: PermissionModuleNode[];\r\n}\r\n\r\nexport interface PermissionModuleNode {\r\n id: string;\r\n code: string;\r\n label: string;\r\n permissionPath: string;\r\n sections: PermissionSectionNode[];\r\n}\r\n\r\nexport interface PermissionSectionNode {\r\n id: string;\r\n code: string;\r\n label: string;\r\n permissionPath: string;\r\n resources: PermissionResourceNode[];\r\n}\r\n\r\nexport interface PermissionResourceNode {\r\n id: string;\r\n code: string;\r\n label: string;\r\n permissionPath: string;\r\n}\r\n\r\n// Matrix endpoint types\r\nexport interface MatrixRoleDto {\r\n id: string;\r\n name: string;\r\n shortName: string;\r\n category: RoleCategory;\r\n description: string | null;\r\n isSystem: boolean;\r\n permissionIds: string[];\r\n}\r\n\r\nexport interface PermissionMatrixDto {\r\n roles: MatrixRoleDto[];\r\n permissions: PermissionListDto[];\r\n tree: PermissionTreeDto;\r\n}\r\n\r\nconst _createRolesApi = (apiContext: ApiContext) => ({\r\n getAll: (params?: { search?: string; excludeCategories?: string; minPermissionsCount?: number }) =>\r\n apiContext.get<RoleListDto[]>('/api/administration/permissions/roles', { params }),\r\n\r\n /** Get roles that can be assigned to groups/users (excludes Custom category and roles with 0 permissions) */\r\n getAssignable: () =>\r\n apiContext.get<RoleListDto[]>('/api/administration/permissions/roles', {\r\n params: { excludeCategories: 'Custom', minPermissionsCount: 1 }\r\n }),\r\n\r\n /** Get roles assignable to external applications/services (only roles with data export permissions) */\r\n getServiceAssignable: () =>\r\n apiContext.get<RoleListDto[]>('/api/administration/permissions/roles', {\r\n params: { forExternalApps: true, minPermissionsCount: 1 }\r\n }),\r\n\r\n /** Get roles scoped to a specific application (for access request approval) */\r\n getByApplication: (applicationId: string) =>\r\n apiContext.get<RoleListDto[]>('/api/administration/permissions/roles', {\r\n params: { applicationId, excludeCategories: 'Custom', minPermissionsCount: 1 }\r\n }),\r\n\r\n getById: (id: string) =>\r\n apiContext.get<RoleDetailDto>(`/api/administration/permissions/roles/${id}`),\r\n\r\n create: (data: CreateRoleRequest) =>\r\n apiContext.post<RoleDetailDto>('/api/administration/permissions/roles', data),\r\n\r\n update: (id: string, data: UpdateRoleRequest) =>\r\n apiContext.put<RoleDetailDto>(`/api/administration/permissions/roles/${id}`, data),\r\n\r\n delete: (id: string) =>\r\n apiContext.delete(`/api/administration/permissions/roles/${id}`),\r\n\r\n assignUser: (roleId: string, userId: string) =>\r\n apiContext.post(`/api/administration/permissions/roles/${roleId}/users/${userId}`),\r\n\r\n removeUser: (roleId: string, userId: string) =>\r\n apiContext.delete(`/api/administration/permissions/roles/${roleId}/users/${userId}`),\r\n\r\n // Groups\r\n getGroups: (roleId: string) =>\r\n apiContext.get<RoleGroupDto[]>(`/api/administration/permissions/roles/${roleId}/groups`),\r\n\r\n assignGroup: (roleId: string, groupId: string) =>\r\n apiContext.post(`/api/administration/permissions/roles/${roleId}/groups/${groupId}`),\r\n\r\n removeGroup: (roleId: string, groupId: string) =>\r\n apiContext.delete(`/api/administration/permissions/roles/${roleId}/groups/${groupId}`),\r\n\r\n // Translations\r\n getTranslations: (roleId: string) =>\r\n apiContext.get<RoleTranslationDto[]>(`/api/administration/permissions/roles/${roleId}/translations`),\r\n\r\n upsertTranslation: (roleId: string, languageCode: string, data: UpdateRoleTranslationRequest) =>\r\n apiContext.put<RoleTranslationDto>(`/api/administration/permissions/roles/${roleId}/translations/${languageCode}`, data),\r\n\r\n deleteTranslation: (roleId: string, languageCode: string) =>\r\n apiContext.delete(`/api/administration/permissions/roles/${roleId}/translations/${languageCode}`)\r\n});\r\nexport type RolesApi = ReturnType<typeof _createRolesApi>;\r\nexport const createRolesApi: (apiContext: ApiContext) => RolesApi = _createRolesApi;\r\n\r\n\r\nconst _createPermissionsApi = (apiContext: ApiContext) => ({\r\n getAll: (params?: { search?: string; level?: string }) =>\r\n apiContext.get<PermissionListDto[]>('/api/administration/permissions', { params }),\r\n\r\n getById: (id: string) =>\r\n apiContext.get<PermissionDetailDto>(`/api/administration/permissions/${id}`),\r\n\r\n getTree: () =>\r\n apiContext.get<PermissionTreeDto>('/api/administration/permissions/tree'),\r\n\r\n getMatrix: () =>\r\n apiContext.get<PermissionMatrixDto>('/api/administration/permissions/matrix'),\r\n\r\n delete: (id: string) =>\r\n apiContext.delete(`/api/administration/permissions/${id}`)\r\n});\r\nexport type PermissionsApi = ReturnType<typeof _createPermissionsApi>;\r\nexport const createPermissionsApi: (apiContext: ApiContext) => PermissionsApi = _createPermissionsApi;\r\n\r\n","import type { ApiContext } from './_shared';\r\n\r\n// Application Types\r\nexport interface ApplicationListDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n description: string | null;\r\n icon: string | null;\r\n iconType: string;\r\n route: string | null;\r\n displayOrder: number;\r\n isActive: boolean;\r\n zone: string;\r\n modulesCount: number;\r\n}\r\n\r\nexport interface ApplicationDetailDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n description: string | null;\r\n icon: string | null;\r\n iconType: string;\r\n route: string | null;\r\n displayOrder: number;\r\n isActive: boolean;\r\n zone: string;\r\n modules: ApplicationModuleDto[];\r\n createdAt: string;\r\n updatedAt: string | null;\r\n}\r\n\r\nexport interface ApplicationModuleDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n description: string | null;\r\n icon: string | null;\r\n iconType: string;\r\n route: string | null;\r\n displayOrder: number;\r\n isActive: boolean;\r\n sectionsCount: number;\r\n}\r\n\r\nexport interface UpdateApplicationRequest {\r\n label: string;\r\n description?: string;\r\n icon?: string;\r\n iconType?: string;\r\n route?: string;\r\n displayOrder: number;\r\n}\r\n\r\nexport interface UpdateModuleRequest {\r\n label: string;\r\n description?: string;\r\n icon?: string;\r\n iconType?: string;\r\n route?: string;\r\n displayOrder: number;\r\n}\r\n\r\nexport interface ModuleSectionDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n description: string | null;\r\n icon: string | null;\r\n iconType: string;\r\n route: string | null;\r\n displayOrder: number;\r\n isActive: boolean;\r\n}\r\n\r\nexport interface ReorderItemDto {\r\n id: string;\r\n displayOrder: number;\r\n}\r\n\r\nexport interface ReorderRequest {\r\n items: ReorderItemDto[];\r\n}\r\n\r\n// Application Tenant Publication Types\r\nexport interface ApplicationTenantDto {\r\n tenantId: string;\r\n tenantName: string;\r\n tenantSlug: string;\r\n tenantType: string;\r\n tenantStatus: string;\r\n accessLevel: string;\r\n assignedAt: string;\r\n assignedBy: string | null;\r\n}\r\n\r\nexport interface AvailableTenantForAppDto {\r\n id: string;\r\n name: string;\r\n slug: string;\r\n type: string;\r\n}\r\n\r\nexport interface AvailableTenantsResponse {\r\n tenants: AvailableTenantForAppDto[];\r\n isInDefaultPersonalTemplate: boolean;\r\n}\r\n\r\nexport type ApplicationAccessLevel = 'FullAccess' | 'RequestAccess' | 'Disabled';\r\n\r\n// Bulk Tenant Assignment Types\r\nexport interface BulkAssignTenantsRequest {\r\n tenantIds: string[];\r\n accessLevel?: ApplicationAccessLevel;\r\n accessMode?: 'full' | 'custom';\r\n moduleIds?: string[];\r\n sectionIds?: string[];\r\n mergeMode?: 'merge' | 'replace';\r\n}\r\n\r\nexport interface BulkAssignResultItem {\r\n tenantId: string;\r\n tenantName: string;\r\n success: boolean;\r\n error?: string;\r\n}\r\n\r\nexport interface BulkAssignTenantsResponse {\r\n successCount: number;\r\n failedCount: number;\r\n results: BulkAssignResultItem[];\r\n}\r\n\r\n// Bulk Application Assignment Types (for assigning applications to a tenant)\r\nexport interface BulkAssignAppsRequest {\r\n applicationIds: string[];\r\n accessLevel: ApplicationAccessLevel;\r\n accessMode: 'full' | 'custom';\r\n moduleIds?: string[];\r\n sectionIds?: string[];\r\n}\r\n\r\nexport interface BulkAssignAppsResultItem {\r\n applicationId: string;\r\n applicationName: string;\r\n success: boolean;\r\n errorMessage?: string;\r\n}\r\n\r\nexport interface BulkAssignAppsResponse {\r\n successCount: number;\r\n failedCount: number;\r\n results: BulkAssignAppsResultItem[];\r\n}\r\n\r\nconst _createApplicationsApi = (apiContext: ApiContext) => ({\r\n getAll: (params?: { contextId?: string; isActive?: boolean }) =>\r\n apiContext.get<ApplicationListDto[]>('/api/administration/applications', { params }),\r\n\r\n getById: (id: string) =>\r\n apiContext.get<ApplicationDetailDto>(`/api/administration/applications/${id}`),\r\n\r\n update: (id: string, data: UpdateApplicationRequest) =>\r\n apiContext.put<ApplicationDetailDto>(`/api/administration/applications/${id}`, data),\r\n\r\n activate: (id: string) =>\r\n apiContext.patch(`/api/administration/applications/${id}/activate`),\r\n\r\n deactivate: (id: string) =>\r\n apiContext.patch(`/api/administration/applications/${id}/deactivate`),\r\n\r\n // Modules\r\n getModules: (appId: string) =>\r\n apiContext.get<ApplicationModuleDto[]>(`/api/administration/applications/${appId}/modules`),\r\n\r\n updateModule: (appId: string, moduleId: string, data: UpdateModuleRequest) =>\r\n apiContext.put<ApplicationModuleDto>(`/api/administration/applications/${appId}/modules/${moduleId}`, data),\r\n\r\n // Module Sections\r\n getModuleSections: (appId: string, moduleId: string) =>\r\n apiContext.get<ModuleSectionDto[]>(`/api/administration/applications/${appId}/modules/${moduleId}/sections`),\r\n\r\n // Reordering\r\n reorderModules: (appId: string, items: ReorderItemDto[]) =>\r\n apiContext.put(`/api/administration/applications/${appId}/modules/reorder`, { items }),\r\n\r\n reorderSections: (appId: string, moduleId: string, items: ReorderItemDto[]) =>\r\n apiContext.put(`/api/administration/applications/${appId}/modules/${moduleId}/sections/reorder`, { items }),\r\n\r\n // Tenant Publication\r\n getTenants: (appId: string) =>\r\n apiContext.get<ApplicationTenantDto[]>(`/api/administration/applications/${appId}/tenants`),\r\n\r\n getAvailableTenants: (appId: string) =>\r\n apiContext.get<AvailableTenantsResponse>(`/api/administration/applications/${appId}/tenants/available`),\r\n\r\n assignToTenant: (appId: string, tenantId: string, accessLevel?: ApplicationAccessLevel) =>\r\n apiContext.post<ApplicationTenantDto>(`/api/administration/applications/${appId}/tenants/${tenantId}`,\r\n accessLevel ? { accessLevel } : {}),\r\n\r\n removeFromTenant: (appId: string, tenantId: string) =>\r\n apiContext.delete(`/api/administration/applications/${appId}/tenants/${tenantId}`),\r\n\r\n updateTenantAccessLevel: (appId: string, tenantId: string, accessLevel: ApplicationAccessLevel) =>\r\n apiContext.patch(`/api/administration/applications/${appId}/tenants/${tenantId}/access-level`, { accessLevel }),\r\n\r\n bulkAssignTenants: (appId: string, request: BulkAssignTenantsRequest) =>\r\n apiContext.post<BulkAssignTenantsResponse>(`/api/administration/applications/${appId}/tenants/bulk`, request)\r\n});\r\nexport type ApplicationsApi = ReturnType<typeof _createApplicationsApi>;\r\nexport const createApplicationsApi: (apiContext: ApiContext) => ApplicationsApi = _createApplicationsApi;\r\n\r\n","import type { ApiContext } from './_shared';\r\n\r\n// Session Types\r\nexport interface SessionDto {\r\n id: string;\r\n loginAt: string;\r\n logoutAt: string | null;\r\n ipAddress: string | null;\r\n userAgent: string | null;\r\n deviceFingerprint: string | null;\r\n deviceType: 'Unknown' | 'Desktop' | 'Mobile' | 'Tablet';\r\n country: string | null;\r\n city: string | null;\r\n isActive: boolean;\r\n duration: string | null;\r\n}\r\n\r\nexport interface UserSessionsDto {\r\n activeSessions: SessionDto[];\r\n history: SessionDto[];\r\n}\r\n\r\n// Activity Types\r\nexport interface LoginDayStats {\r\n date: string;\r\n successful: number;\r\n failed: number;\r\n}\r\n\r\nexport interface DeviceStats {\r\n device: string;\r\n count: number;\r\n}\r\n\r\nexport interface LocationStats {\r\n location: string;\r\n count: number;\r\n}\r\n\r\nexport interface UserActivityDto {\r\n totalSuccessfulLogins: number;\r\n totalFailedLogins: number;\r\n lastSuccessfulLoginAt: string | null;\r\n lastSuccessfulLoginIp: string | null;\r\n lastFailedLoginAt: string | null;\r\n lastFailedLoginIp: string | null;\r\n lastFailedLoginReason: string | null;\r\n loginsByDay: LoginDayStats[];\r\n deviceStats: DeviceStats[];\r\n locationStats: LocationStats[];\r\n}\r\n\r\n// Dashboard Types\r\nexport interface UserDashboardOverviewDto {\r\n totalUsers: number;\r\n activeUsers: number;\r\n inactiveUsers: number;\r\n newUsers: number;\r\n totalSessions: number;\r\n failedLogins: number;\r\n usersLoggedInRecently: number;\r\n totalRoles: number;\r\n totalPermissions: number;\r\n}\r\n\r\nexport interface UserStatusCountDto {\r\n status: string;\r\n count: number;\r\n}\r\n\r\nexport interface UserRoleDistributionDto {\r\n roleName: string;\r\n count: number;\r\n}\r\n\r\nexport interface UserTypeDistributionDto {\r\n userType: string;\r\n count: number;\r\n}\r\n\r\nexport interface LoginActivityStatsDto {\r\n totalSuccessful: number;\r\n totalFailed: number;\r\n byDay: LoginDayActivityDto[];\r\n}\r\n\r\nexport interface LoginDayActivityDto {\r\n date: string;\r\n successful: number;\r\n failed: number;\r\n}\r\n\r\nexport interface UserTrendDto {\r\n date: string;\r\n newUsers: number;\r\n logins: number;\r\n}\r\n\r\nexport interface RecentUserDto {\r\n id: string;\r\n email: string;\r\n fullName: string;\r\n createdAt: string;\r\n lastLoginAt: string | null;\r\n isActive: boolean;\r\n userType: string;\r\n}\r\n\r\nexport interface SecurityAlertDto {\r\n userId: string;\r\n email: string;\r\n fullName: string;\r\n failedAttempts: number;\r\n lastAttemptAt: string;\r\n ipAddresses: string[];\r\n}\r\n\r\nexport interface ActiveSessionsStatsDto {\r\n totalActiveSessions: number;\r\n byBrowser: BrowserStatsDto[];\r\n byCountry: CountryStatsDto[];\r\n}\r\n\r\nexport interface BrowserStatsDto {\r\n browser: string;\r\n count: number;\r\n}\r\n\r\nexport interface CountryStatsDto {\r\n country: string;\r\n count: number;\r\n}\r\n\r\nexport interface LatestActiveSessionDto {\r\n userId: string;\r\n fullName: string;\r\n duration: string;\r\n loginAt: string;\r\n}\r\n\r\nconst _createDashboardApi = (apiContext: ApiContext) => ({\r\n getOverview: (days: number = 30, config?: { signal?: AbortSignal }) =>\r\n apiContext.get<UserDashboardOverviewDto>('/api/administration/users/dashboard/overview', { params: { days }, signal: config?.signal }),\r\n\r\n getByStatus: (config?: { signal?: AbortSignal }) =>\r\n apiContext.get<UserStatusCountDto[]>('/api/administration/users/dashboard/by-status', { signal: config?.signal }),\r\n\r\n getByRole: (config?: { signal?: AbortSignal }) =>\r\n apiContext.get<UserRoleDistributionDto[]>('/api/administration/users/dashboard/by-role', { signal: config?.signal }),\r\n\r\n getByType: (config?: { signal?: AbortSignal }) =>\r\n apiContext.get<UserTypeDistributionDto[]>('/api/administration/users/dashboard/by-type', { signal: config?.signal }),\r\n\r\n getLoginActivity: (days: number = 30, config?: { signal?: AbortSignal }) =>\r\n apiContext.get<LoginActivityStatsDto>('/api/administration/users/dashboard/login-activity', { params: { days }, signal: config?.signal }),\r\n\r\n getTrends: (days: number = 30, config?: { signal?: AbortSignal }) =>\r\n apiContext.get<UserTrendDto[]>('/api/administration/users/dashboard/trends', { params: { days }, signal: config?.signal }),\r\n\r\n getRecentUsers: (limit: number = 10, config?: { signal?: AbortSignal }) =>\r\n apiContext.get<RecentUserDto[]>('/api/administration/users/dashboard/recent-users', { params: { limit }, signal: config?.signal }),\r\n\r\n getSecurityAlerts: (threshold: number = 3, hours: number = 24, config?: { signal?: AbortSignal }) =>\r\n apiContext.get<SecurityAlertDto[]>('/api/administration/users/dashboard/security-alerts', { params: { threshold, hours }, signal: config?.signal }),\r\n\r\n getActiveSessions: (config?: { signal?: AbortSignal }) =>\r\n apiContext.get<ActiveSessionsStatsDto>('/api/administration/users/dashboard/active-sessions', { signal: config?.signal }),\r\n\r\n getLatestActiveSessions: (limit: number = 5, config?: { signal?: AbortSignal }) =>\r\n apiContext.get<LatestActiveSessionDto[]>('/api/administration/users/dashboard/latest-active-sessions', { params: { limit }, signal: config?.signal })\r\n});\r\nexport type DashboardApi = ReturnType<typeof _createDashboardApi>;\r\nexport const createDashboardApi: (apiContext: ApiContext) => DashboardApi = _createDashboardApi;\r\n\r\n","import type { ApiContext } from './_shared';\r\n\r\n// Preferences Scope Types\r\nexport type PreferencesScope = 'tenant' | 'global' | 'system';\r\n\r\nexport interface PreferencesScopeInfo {\r\n effectiveScope: PreferencesScope;\r\n hasTenantOverride: boolean;\r\n hasGlobalPreferences: boolean;\r\n currentTenantId: string | null;\r\n}\r\n\r\nexport interface BorderRadiusDto {\r\n card: string;\r\n button: string;\r\n badge: string;\r\n input: string;\r\n modal: string;\r\n menuItem: string;\r\n}\r\n\r\nexport interface UserPreferencesWithScopeDto {\r\n theme: string;\r\n language: string;\r\n timeZone: string | null;\r\n emailNotifications: boolean;\r\n pushNotifications: boolean;\r\n accentColorKey: string;\r\n borderRadius: BorderRadiusDto;\r\n itemPaletteKey: string;\r\n scope: PreferencesScopeInfo;\r\n}\r\n\r\n// Settings Types\r\nexport type SettingSource = 'Database' | 'AppSettings' | 'Environment' | 'Computed';\r\nexport type SettingValueType = 'String' | 'Int' | 'Bool' | 'Json' | 'TimeSpan' | 'Decimal';\r\n\r\nexport interface SettingDto {\r\n category: string;\r\n key: string;\r\n value: string;\r\n defaultValue: string;\r\n valueType: SettingValueType;\r\n description: string | null;\r\n isEditable: boolean;\r\n isSensitive: boolean;\r\n displayOrder: number;\r\n}\r\n\r\nexport interface SettingViewModel {\r\n category: string;\r\n key: string;\r\n value: string;\r\n displayValue: string;\r\n defaultValue: string | null;\r\n valueType: SettingValueType;\r\n description: string | null;\r\n isEditable: boolean;\r\n isSensitive: boolean;\r\n displayOrder: number;\r\n source: SettingSource;\r\n modifiedByUserName: string | null;\r\n modifiedAt: string | null;\r\n}\r\n\r\nexport interface SettingHistoryDto {\r\n id: string;\r\n oldValue: string;\r\n newValue: string;\r\n changedByUserName: string | null;\r\n changedAt: string;\r\n}\r\n\r\nconst _createSettingsApi = (apiContext: ApiContext) => ({\r\n getAll: () =>\r\n apiContext.get<SettingViewModel[]>('/api/administration/configuration/settings'),\r\n\r\n getByCategory: (category: string) =>\r\n apiContext.get<SettingViewModel[]>(`/api/administration/configuration/settings/${category}`),\r\n\r\n get: (category: string, key: string) =>\r\n apiContext.get<SettingDto>(`/api/administration/configuration/settings/${category}/${key}`),\r\n\r\n update: (category: string, key: string, value: string) =>\r\n apiContext.put<SettingDto>(`/api/administration/configuration/settings/${category}/${key}`, { value }),\r\n\r\n reset: (category: string, key: string) =>\r\n apiContext.post<SettingDto>(`/api/administration/configuration/settings/${category}/${key}/reset`),\r\n\r\n getHistory: (category: string, key: string) =>\r\n apiContext.get<SettingHistoryDto[]>(`/api/administration/configuration/settings/${category}/${key}/history`),\r\n\r\n getCategories: () =>\r\n apiContext.get<string[]>('/api/administration/configuration/settings/categories')\r\n});\r\nexport type SettingsApi = ReturnType<typeof _createSettingsApi>;\r\nexport const createSettingsApi: (apiContext: ApiContext) => SettingsApi = _createSettingsApi;\r\n\r\n\r\nconst _createPreferencesApi = (apiContext: ApiContext) => ({\r\n /** Get preferences with scope info (effective scope, has tenant override, etc.) */\r\n get: () =>\r\n apiContext.get<UserPreferencesWithScopeDto>('/api/user/preferences'),\r\n\r\n /** Get global preferences only (TenantId = null), ignoring tenant-specific overrides */\r\n getGlobal: () =>\r\n apiContext.get<UserPreferencesWithScopeDto>('/api/user/preferences/global'),\r\n\r\n /** Get preferences for a specific tenant (for editing without switching context) */\r\n getForTenant: (tenantId: string) =>\r\n apiContext.get<UserPreferencesWithScopeDto>(`/api/user/preferences/for-tenant/${tenantId}`),\r\n\r\n /** Get list of tenant IDs that have theme overrides */\r\n getThemeOverrides: () =>\r\n apiContext.get<string[]>('/api/user/preferences/theme-overrides'),\r\n\r\n /** Delete tenant-specific preferences, reverting to global */\r\n deleteTenantOverride: () =>\r\n apiContext.delete<UserPreferencesWithScopeDto>('/api/user/preferences/tenant-override'),\r\n\r\n /** Create tenant-specific preferences from current effective preferences */\r\n applyToTenant: () =>\r\n apiContext.post<UserPreferencesWithScopeDto>('/api/user/preferences/apply-to-tenant')\r\n});\r\nexport type PreferencesApi = ReturnType<typeof _createPreferencesApi>;\r\nexport const createPreferencesApi: (apiContext: ApiContext) => PreferencesApi = _createPreferencesApi;\r\n\r\n","import type { ApiContext, PaginatedResult } from './_shared';\r\nimport { apiClient } from './_shared';\r\nimport type { ApplicationAccessLevel, BulkAssignAppsRequest, BulkAssignAppsResponse } from './applications';\r\nimport type { UserTenantDto } from './crossCutting';\r\n\r\n// Tenant Types\r\nexport type TenantStatus = 'Pending' | 'Active' | 'Suspended' | 'Archived';\r\nexport type TenantType = 'Personal' | 'Business' | 'System';\r\n\r\nexport interface TenantListDto {\r\n id: string;\r\n name: string;\r\n slug: string;\r\n type: TenantType;\r\n status: TenantStatus;\r\n description: string | null;\r\n organisationId: string | null;\r\n organisation: { id: string; name: string } | null;\r\n templateId: string | null;\r\n templateName: string | null;\r\n templateCode: string | null;\r\n memberCount: number;\r\n applicationCount: number;\r\n pendingAccessRequestCount: number;\r\n createdAt: string;\r\n}\r\n\r\nexport interface TenantOrganisationDto {\r\n id: string;\r\n name: string;\r\n legalName: string | null;\r\n countryCode: string;\r\n businessIdentifier: string;\r\n businessIdentifierType: string;\r\n vatNumber: string | null;\r\n address: {\r\n street: string;\r\n street2: string | null;\r\n postalCode: string;\r\n city: string;\r\n state: string | null;\r\n country: string;\r\n countryCode: string;\r\n };\r\n phone: string | null;\r\n email: string | null;\r\n website: string | null;\r\n verificationStatus: string;\r\n verifiedAt: string | null;\r\n verificationNotes: string | null;\r\n}\r\n\r\nexport interface TenantDetailDto {\r\n id: string;\r\n name: string;\r\n slug: string;\r\n type: TenantType;\r\n status: TenantStatus;\r\n description: string | null;\r\n organisationId: string | null;\r\n organisation: TenantOrganisationDto | null;\r\n templateId: string | null;\r\n templateName: string | null;\r\n templateCode: string | null;\r\n createdAt: string;\r\n createdBy: string | null;\r\n updatedAt: string | null;\r\n updatedBy: string | null;\r\n activatedAt: string | null;\r\n suspendedAt: string | null;\r\n archivedAt: string | null;\r\n memberCount: number;\r\n applicationCount: number;\r\n pendingAccessRequestCount: number;\r\n}\r\n\r\nexport interface TenantMemberDto {\r\n /** User ID - use this as memberId for API calls (backend: \"memberId is actually the userId\") */\r\n userId: string;\r\n userEmail: string | null;\r\n userDisplayName: string | null;\r\n isOwner: boolean;\r\n joinedAt: string;\r\n roles: TenantMemberRoleDto[];\r\n /** How the user has access to this tenant: Direct, Group, or Both */\r\n membershipSource: 'Direct' | 'Group' | 'Both';\r\n /** The primary group granting access (if via group) */\r\n primaryGroupId: string | null;\r\n /** The name of the primary group granting access (if via group) */\r\n primaryGroupName: string | null;\r\n}\r\n\r\nexport interface TenantMemberRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n assignedAt: string;\r\n assignedBy: string | null;\r\n}\r\n\r\nexport interface TenantMemberCustomRoleDto {\r\n id: string;\r\n name: string;\r\n shortName: string;\r\n code: string | null;\r\n category: string;\r\n description: string | null;\r\n isSystem: boolean;\r\n permissions: TenantMemberCustomRolePermissionDto[];\r\n createdAt: string;\r\n updatedAt: string | null;\r\n}\r\n\r\nexport interface TenantMemberCustomRolePermissionDto {\r\n id: string;\r\n path: string;\r\n description: string | null;\r\n}\r\n\r\n// Tenant Group Types\r\nexport interface TenantGroupDto {\r\n groupId: string;\r\n name: string;\r\n description: string | null;\r\n isActive: boolean;\r\n isSecurityGroup: boolean;\r\n isSyncedFromEntra: boolean;\r\n entraObjectId: string | null;\r\n memberCount: number;\r\n roleCount: number;\r\n isDirect: boolean;\r\n linkedViaGroupId: string | null;\r\n linkedViaGroupName: string | null;\r\n addedAt: string | null;\r\n addedBy: string | null;\r\n}\r\n\r\nexport interface AvailableGroupDto {\r\n groupId: string;\r\n name: string;\r\n description: string | null;\r\n isSecurityGroup: boolean;\r\n isSyncedFromEntra: boolean;\r\n memberCount: number;\r\n}\r\n\r\nexport type TenantApplicationSource = 'Inherited' | 'Direct' | 'Overridden' | 'Revoked';\r\n\r\nexport interface TenantApplicationDto {\r\n applicationId: string;\r\n label: string;\r\n code: string;\r\n description: string | null;\r\n icon: string | null;\r\n accessLevel: ApplicationAccessLevel;\r\n assignedAt: string;\r\n assignedBy: string | null;\r\n source: TenantApplicationSource;\r\n inheritedModuleCount: number;\r\n directModuleCount: number;\r\n revokedModuleCount: number;\r\n inheritedSectionCount: number;\r\n directSectionCount: number;\r\n revokedSectionCount: number;\r\n totalModuleCount: number;\r\n totalSectionCount: number;\r\n}\r\n\r\n// Effective tree types for override management\r\nexport interface EffectiveModuleNode {\r\n moduleId: string;\r\n label: string;\r\n code: string;\r\n icon: string | null;\r\n source: TenantApplicationSource | 'Unavailable';\r\n accessLevel: string;\r\n isEffectivelyActive: boolean;\r\n sections: EffectiveSectionNode[];\r\n}\r\n\r\nexport interface EffectiveSectionNode {\r\n sectionId: string;\r\n label: string;\r\n code: string;\r\n icon: string | null;\r\n source: TenantApplicationSource | 'Unavailable';\r\n accessLevel: string;\r\n isEffectivelyActive: boolean;\r\n}\r\n\r\n// Override request types\r\nexport type OverrideAction = 'add' | 'revoke' | 'remove-override';\r\n\r\nexport interface ModuleOverrideItem {\r\n moduleId: string;\r\n action: OverrideAction;\r\n}\r\n\r\nexport interface SectionOverrideItem {\r\n sectionId: string;\r\n action: OverrideAction;\r\n}\r\n\r\nexport interface SaveOverridesRequest {\r\n modules?: ModuleOverrideItem[];\r\n sections?: SectionOverrideItem[];\r\n}\r\n\r\nexport interface AvailableApplicationDto {\r\n id: string;\r\n label: string;\r\n code: string;\r\n description: string | null;\r\n icon: string | null;\r\n isAssigned: boolean;\r\n}\r\n\r\n// Tenant Role Types (filtered by accessible applications)\r\nexport interface TenantRoleDto {\r\n id: string;\r\n name: string;\r\n shortName: string;\r\n code: string | null;\r\n category: string;\r\n description: string | null;\r\n isSystem: boolean;\r\n applicationId: string | null;\r\n applicationName: string | null;\r\n permissionsCount: number;\r\n assignedMembersCount: number;\r\n}\r\n\r\n// Tenant Module Types\r\nexport interface TenantModuleDto {\r\n moduleId: string;\r\n label: string;\r\n code: string;\r\n description: string | null;\r\n icon: string | null;\r\n accessLevel: string;\r\n assignedAt: string;\r\n assignedBy: string | null;\r\n}\r\n\r\nexport interface AvailableModuleDto {\r\n id: string;\r\n label: string;\r\n code: string;\r\n description: string | null;\r\n icon: string | null;\r\n applicationLabel: string;\r\n isAssigned: boolean;\r\n}\r\n\r\n// Tenant Section Types\r\nexport interface TenantSectionDto {\r\n sectionId: string;\r\n label: string;\r\n code: string;\r\n description: string | null;\r\n icon: string | null;\r\n accessLevel: string;\r\n assignedAt: string;\r\n assignedBy: string | null;\r\n}\r\n\r\nexport interface AvailableSectionDto {\r\n id: string;\r\n label: string;\r\n code: string;\r\n description: string | null;\r\n icon: string | null;\r\n moduleLabel: string;\r\n isAssigned: boolean;\r\n}\r\n\r\n// Module with Sections (for tree view)\r\nexport interface ModuleWithSectionsDto {\r\n moduleId: string;\r\n label: string;\r\n code: string;\r\n icon: string | null;\r\n isAssigned: boolean;\r\n accessLevel: string | null;\r\n sections: SectionAssignmentDto[];\r\n}\r\n\r\nexport interface SectionAssignmentDto {\r\n sectionId: string;\r\n label: string;\r\n code: string;\r\n icon: string | null;\r\n isAssigned: boolean;\r\n accessLevel: string | null;\r\n}\r\n\r\nexport interface CreateTenantB2CRequest {\r\n name: string;\r\n slug: string;\r\n description?: string;\r\n templateId?: string;\r\n}\r\n\r\n// Tenant Template Types\r\n\r\nexport interface TenantTemplateDto {\r\n id: string;\r\n code: string;\r\n name: string;\r\n description: string | null;\r\n targetTenantType: TenantType;\r\n isDefault: boolean;\r\n isActive: boolean;\r\n tenantCount: number;\r\n applicationCount: number;\r\n createdAt: string;\r\n}\r\n\r\nexport interface TenantTemplateDetailDto {\r\n id: string;\r\n code: string;\r\n name: string;\r\n description: string | null;\r\n targetTenantType: TenantType;\r\n isDefault: boolean;\r\n isActive: boolean;\r\n createdAt: string;\r\n applications: TemplateApplicationDto[];\r\n modules: TemplateModuleDto[];\r\n sections: TemplateSectionDto[];\r\n}\r\n\r\nexport interface TemplateApplicationDto {\r\n applicationId: string;\r\n label: string;\r\n code: string;\r\n icon: string | null;\r\n accessLevel: string;\r\n displayOrder: number;\r\n}\r\n\r\nexport interface TemplateModuleDto {\r\n moduleId: string;\r\n label: string;\r\n code: string;\r\n icon: string | null;\r\n displayOrder: number;\r\n}\r\n\r\nexport interface TemplateSectionDto {\r\n sectionId: string;\r\n label: string;\r\n code: string;\r\n icon: string | null;\r\n displayOrder: number;\r\n}\r\n\r\nexport interface CreateTenantTemplateRequest {\r\n code: string;\r\n name: string;\r\n description?: string;\r\n targetTenantType: TenantType;\r\n}\r\n\r\nexport interface UpdateTenantTemplateRequest {\r\n name: string;\r\n description?: string;\r\n}\r\n\r\nexport interface BulkAssignTemplateAppsRequest {\r\n applicationIds: string[];\r\n accessLevel?: string;\r\n accessMode?: string;\r\n moduleIds?: string[];\r\n sectionIds?: string[];\r\n}\r\n\r\nexport interface TemplateApplicationEnrichedDto {\r\n applicationId: string;\r\n label: string;\r\n code: string;\r\n description: string | null;\r\n icon: string | null;\r\n accessLevel: string;\r\n displayOrder: number;\r\n assignedAt: string;\r\n assignedBy: string | null;\r\n assignedModuleCount: number;\r\n assignedSectionCount: number;\r\n totalModuleCount: number;\r\n totalSectionCount: number;\r\n}\r\n\r\nexport interface TenantBasicDto {\r\n id: string;\r\n slug: string;\r\n name: string;\r\n createdAt: string;\r\n}\r\n\r\nexport interface TemplateAppModuleTreeDto {\r\n moduleId: string;\r\n label: string;\r\n code: string;\r\n icon: string | null;\r\n isAssigned: boolean;\r\n sections: TemplateAppSectionTreeDto[];\r\n}\r\n\r\nexport interface TemplateAppSectionTreeDto {\r\n sectionId: string;\r\n label: string;\r\n code: string;\r\n icon: string | null;\r\n isAssigned: boolean;\r\n}\r\n\r\nexport interface UpdateTemplateAppModulesRequest {\r\n accessMode: 'full' | 'custom';\r\n moduleIds?: string[];\r\n sectionIds?: string[];\r\n}\r\n\r\nexport interface TemplateAiInstanceDto {\r\n id: string;\r\n instanceId: string;\r\n instanceCode: string;\r\n instanceName: string;\r\n isActive: boolean;\r\n displayOrder: number;\r\n assignedAt: string;\r\n assignedBy: string | null;\r\n}\r\n\r\n// Business Identifier Types (matching backend enum)\r\nexport type BusinessIdentifierType = 'SIREN' | 'SIRET' | 'UID_CHE' | 'HRB' | 'HRA' | 'BCE_KBO' | 'KVK' | 'CIF' | 'PartitaIVA' | 'CRN' | 'VAT' | 'ABN' | 'Other';\r\n\r\nexport interface CreateAddressRequest {\r\n street: string;\r\n street2?: string;\r\n postalCode: string;\r\n city: string;\r\n state?: string;\r\n country: string;\r\n countryCode: string;\r\n}\r\n\r\nexport interface CreateOrganisationRequest {\r\n name: string;\r\n legalName?: string;\r\n countryCode: string;\r\n businessIdentifierType: BusinessIdentifierType;\r\n businessIdentifier: string;\r\n vatNumber?: string;\r\n address: CreateAddressRequest;\r\n phone?: string;\r\n email?: string;\r\n website?: string;\r\n isFromRegistry?: boolean;\r\n}\r\n\r\nexport type VerificationStatus = 'Pending' | 'Verified' | 'Rejected';\r\n\r\nexport interface OrganisationListItemDto {\r\n id: string;\r\n name: string;\r\n legalName: string | null;\r\n countryCode: string;\r\n businessIdentifier: string;\r\n businessIdentifierType: BusinessIdentifierType;\r\n verificationStatus: VerificationStatus;\r\n verifiedAt: string | null;\r\n createdAt: string;\r\n}\r\n\r\nexport interface AddressDto {\r\n street: string;\r\n street2: string | null;\r\n postalCode: string;\r\n city: string;\r\n state: string | null;\r\n country: string;\r\n countryCode: string;\r\n}\r\n\r\nexport interface OrganisationDto {\r\n id: string;\r\n name: string;\r\n legalName: string | null;\r\n countryCode: string;\r\n businessIdentifier: string;\r\n businessIdentifierType: BusinessIdentifierType;\r\n vatNumber: string | null;\r\n address: AddressDto | null;\r\n phone: string | null;\r\n email: string | null;\r\n website: string | null;\r\n verificationStatus: VerificationStatus;\r\n verifiedAt: string | null;\r\n verificationNotes: string | null;\r\n verifiedBy: string | null;\r\n createdAt: string;\r\n createdBy: string | null;\r\n updatedAt: string | null;\r\n updatedBy: string | null;\r\n}\r\n\r\nexport interface CreateTenantB2BRequest {\r\n name: string;\r\n slug: string;\r\n description?: string;\r\n organisation?: CreateOrganisationRequest;\r\n organisationId?: string;\r\n}\r\n\r\nexport interface CompanyLookupResponse {\r\n identifier: string;\r\n name: string;\r\n legalName: string | null;\r\n legalForm: string | null;\r\n street: string | null;\r\n street2: string | null;\r\n postalCode: string | null;\r\n city: string | null;\r\n state: string | null;\r\n country: string | null;\r\n countryCode: string | null;\r\n vatNumber: string | null;\r\n phone: string | null;\r\n email: string | null;\r\n website: string | null;\r\n status: string | null;\r\n registryUrl: string | null;\r\n}\r\n\r\nexport interface UpdateTenantRequest {\r\n name: string;\r\n description?: string;\r\n type?: TenantType;\r\n}\r\n\r\nexport interface AddTenantMemberRequest {\r\n userId: string;\r\n isOwner?: boolean;\r\n}\r\n\r\nexport interface AssignTenantRoleRequest {\r\n roleId: string;\r\n}\r\n\r\nexport interface MultitenantConfigResponse {\r\n enableB2B: boolean;\r\n enableB2C: boolean;\r\n}\r\n\r\nconst _createTenantsApi = (apiContext: ApiContext) => ({\r\n getConfig: () =>\r\n apiContext.get<MultitenantConfigResponse>('/api/administration/tenants/config'),\r\n\r\n getAll: (params?: { page?: number; pageSize?: number; search?: string; status?: TenantStatus; type?: TenantType; sortBy?: string; sortDesc?: boolean; excludeTypes?: TenantType[] }) => {\r\n const { excludeTypes, ...rest } = params || {};\r\n const searchParams = new URLSearchParams();\r\n if (rest.page) searchParams.set('page', String(rest.page));\r\n if (rest.pageSize) searchParams.set('pageSize', String(rest.pageSize));\r\n if (rest.search) searchParams.set('search', rest.search);\r\n if (rest.status) searchParams.set('status', rest.status);\r\n if (rest.type) searchParams.set('type', rest.type);\r\n if (rest.sortBy) searchParams.set('sortBy', rest.sortBy);\r\n if (rest.sortDesc) searchParams.set('sortDesc', 'true');\r\n if (excludeTypes?.length) {\r\n excludeTypes.forEach(t => searchParams.append('excludeTypes', t));\r\n }\r\n const qs = searchParams.toString();\r\n return apiContext.get<PaginatedResult<TenantListDto>>(`/api/administration/tenants${qs ? `?${qs}` : ''}`);\r\n },\r\n\r\n getById: (id: string) =>\r\n apiContext.get<TenantDetailDto>(`/api/administration/tenants/${id}`),\r\n\r\n createB2C: (data: CreateTenantB2CRequest) =>\r\n apiContext.post<TenantDetailDto>('/api/administration/tenants/b2c', data),\r\n\r\n createB2B: (data: CreateTenantB2BRequest) =>\r\n apiContext.post<TenantDetailDto>('/api/administration/tenants/b2b', data),\r\n\r\n update: (id: string, data: UpdateTenantRequest) =>\r\n apiContext.put<TenantDetailDto>(`/api/administration/tenants/${id}`, data),\r\n\r\n delete: (id: string) =>\r\n apiContext.delete(`/api/administration/tenants/${id}`),\r\n\r\n activate: (id: string) =>\r\n apiContext.post(`/api/administration/tenants/${id}/activate`),\r\n\r\n suspend: (id: string) =>\r\n apiContext.post(`/api/administration/tenants/${id}/suspend`),\r\n\r\n archive: (id: string) =>\r\n apiContext.delete(`/api/administration/tenants/${id}`),\r\n\r\n checkSlugAvailable: (slug: string) =>\r\n apiContext.get<{ available: boolean }>(`/api/administration/tenants/check-slug/${slug}`),\r\n\r\n // Template\r\n assignTemplate: (tenantId: string, templateId: string) =>\r\n apiContext.post(`/api/administration/tenants/${tenantId}/template`, { templateId }),\r\n\r\n removeTemplate: (tenantId: string) =>\r\n apiContext.delete(`/api/administration/tenants/${tenantId}/template`),\r\n\r\n // Members\r\n getMembers: (tenantId: string) =>\r\n apiContext.get<TenantMemberDto[]>(`/api/administration/tenants/${tenantId}/members`),\r\n\r\n getMember: (tenantId: string, memberId: string) =>\r\n apiContext.get<TenantMemberDto>(`/api/administration/tenants/${tenantId}/members/${memberId}`),\r\n\r\n addMember: (tenantId: string, data: AddTenantMemberRequest) =>\r\n apiContext.post<TenantMemberDto>(`/api/administration/tenants/${tenantId}/members`, data),\r\n\r\n removeMember: (tenantId: string, userId: string) =>\r\n apiContext.delete(`/api/administration/tenants/${tenantId}/members/${userId}`),\r\n\r\n setMemberDefault: (tenantId: string, userId: string) =>\r\n apiContext.patch(`/api/administration/tenants/${tenantId}/members/${userId}/set-default`),\r\n\r\n transferOwnership: (tenantId: string, toUserId: string) =>\r\n apiContext.post(`/api/administration/tenants/${tenantId}/members/transfer-ownership`, { toUserId }),\r\n\r\n // Member Roles\r\n assignRole: (tenantId: string, tenantUserId: string, roleId: string) =>\r\n apiContext.post(`/api/administration/tenants/${tenantId}/members/${tenantUserId}/roles/${roleId}`),\r\n\r\n removeRole: (tenantId: string, tenantUserId: string, roleId: string) =>\r\n apiContext.delete(`/api/administration/tenants/${tenantId}/members/${tenantUserId}/roles/${roleId}`),\r\n\r\n assignMemberRoles: (tenantId: string, tenantUserId: string, roleIds: string[]) =>\r\n apiContext.put(`/api/administration/tenants/${tenantId}/members/${tenantUserId}/roles`, { roleIds }),\r\n\r\n // Member Custom Role (tenant-scoped)\r\n getMemberCustomRole: (tenantId: string, memberId: string) =>\r\n apiContext.get<TenantMemberCustomRoleDto | null>(`/api/administration/tenants/${tenantId}/members/${memberId}/custom-role`),\r\n\r\n createMemberCustomRole: (tenantId: string, memberId: string) =>\r\n apiContext.post<TenantMemberCustomRoleDto>(`/api/administration/tenants/${tenantId}/members/${memberId}/custom-role`),\r\n\r\n deleteMemberCustomRole: (tenantId: string, memberId: string) =>\r\n apiContext.delete(`/api/administration/tenants/${tenantId}/members/${memberId}/custom-role`),\r\n\r\n // Roles (filtered by tenant accessible applications)\r\n getTenantRoles: (tenantId: string) =>\r\n apiContext.get<TenantRoleDto[]>(`/api/administration/tenants/${tenantId}/roles`),\r\n\r\n // Groups\r\n getGroups: (tenantId: string) =>\r\n apiContext.get<TenantGroupDto[]>(`/api/administration/tenants/${tenantId}/groups`),\r\n\r\n getAvailableGroups: (tenantId: string) =>\r\n apiContext.get<AvailableGroupDto[]>(`/api/administration/tenants/${tenantId}/groups/available`),\r\n\r\n addGroupToTenant: (tenantId: string, groupId: string) =>\r\n apiContext.post(`/api/administration/tenants/${tenantId}/groups`, { groupId }),\r\n\r\n removeGroupFromTenant: (tenantId: string, groupId: string) =>\r\n apiContext.delete(`/api/administration/tenants/${tenantId}/groups/${groupId}`),\r\n\r\n // Applications\r\n getApplications: (tenantId: string) =>\r\n apiContext.get<TenantApplicationDto[]>(`/api/administration/tenants/${tenantId}/applications`),\r\n\r\n getAvailableApplications: (tenantId: string) =>\r\n apiContext.get<AvailableApplicationDto[]>(`/api/administration/tenants/${tenantId}/applications/available`),\r\n\r\n assignApplication: (tenantId: string, applicationId: string) =>\r\n apiContext.post<TenantApplicationDto>(`/api/administration/tenants/${tenantId}/applications/${applicationId}`),\r\n\r\n removeApplication: (tenantId: string, applicationId: string) =>\r\n apiContext.delete(`/api/administration/tenants/${tenantId}/applications/${applicationId}`),\r\n\r\n bulkAssignApplications: (tenantId: string, request: BulkAssignAppsRequest) =>\r\n apiContext.post<BulkAssignAppsResponse>(`/api/administration/tenants/${tenantId}/applications/bulk`, request),\r\n\r\n toggleApplication: (tenantId: string, applicationId: string, isActive: boolean) =>\r\n apiContext.patch(`/api/administration/tenants/${tenantId}/applications/${applicationId}/toggle`, { isActive }),\r\n\r\n updateApplicationAccessLevel: (tenantId: string, applicationId: string, accessLevel: ApplicationAccessLevel) =>\r\n apiContext.patch(`/api/administration/tenants/${tenantId}/applications/${applicationId}/access-level`, { accessLevel }),\r\n\r\n // Effective tree (override management)\r\n getEffectiveTree: (tenantId: string, applicationId: string) =>\r\n apiContext.get<EffectiveModuleNode[]>(`/api/administration/tenants/${tenantId}/applications/${applicationId}/effective-tree`),\r\n\r\n saveOverrides: (tenantId: string, applicationId: string, request: SaveOverridesRequest) =>\r\n apiContext.put(`/api/administration/tenants/${tenantId}/applications/${applicationId}/overrides`, request),\r\n\r\n // Modules (granular access within applications)\r\n getModules: (tenantId: string) =>\r\n apiContext.get<TenantModuleDto[]>(`/api/administration/tenants/${tenantId}/modules`),\r\n\r\n getAvailableModules: (tenantId: string) =>\r\n apiContext.get<AvailableModuleDto[]>(`/api/administration/tenants/${tenantId}/modules/available`),\r\n\r\n assignModule: (tenantId: string, moduleId: string) =>\r\n apiContext.post<TenantModuleDto>(`/api/administration/tenants/${tenantId}/modules/${moduleId}`),\r\n\r\n removeModule: (tenantId: string, moduleId: string) =>\r\n apiContext.delete(`/api/administration/tenants/${tenantId}/modules/${moduleId}`),\r\n\r\n toggleModule: (tenantId: string, moduleId: string, isActive: boolean) =>\r\n apiContext.patch(`/api/administration/tenants/${tenantId}/modules/${moduleId}/toggle`, { isActive }),\r\n\r\n // Sections (most granular access level)\r\n getSections: (tenantId: string) =>\r\n apiContext.get<TenantSectionDto[]>(`/api/administration/tenants/${tenantId}/sections`),\r\n\r\n getAvailableSections: (tenantId: string) =>\r\n apiContext.get<AvailableSectionDto[]>(`/api/administration/tenants/${tenantId}/sections/available`),\r\n\r\n assignSection: (tenantId: string, sectionId: string) =>\r\n apiContext.post<TenantSectionDto>(`/api/administration/tenants/${tenantId}/sections/${sectionId}`),\r\n\r\n removeSection: (tenantId: string, sectionId: string) =>\r\n apiContext.delete(`/api/administration/tenants/${tenantId}/sections/${sectionId}`),\r\n\r\n toggleSection: (tenantId: string, sectionId: string, isActive: boolean) =>\r\n apiContext.patch(`/api/administration/tenants/${tenantId}/sections/${sectionId}/toggle`, { isActive }),\r\n\r\n // Tree view (modules with nested sections)\r\n getModulesWithSections: (tenantId: string, applicationId: string) =>\r\n apiContext.get<ModuleWithSectionsDto[]>(`/api/administration/tenants/${tenantId}/applications/${applicationId}/modules-tree`),\r\n\r\n // Organisation\r\n linkOrganisation: (tenantId: string, organisationId: string) =>\r\n apiContext.post(`/api/administration/tenants/${tenantId}/organisation`, { organisationId }),\r\n\r\n unlinkOrganisation: (tenantId: string) =>\r\n apiContext.delete(`/api/administration/tenants/${tenantId}/organisation`)\r\n});\r\nexport type TenantsApi = ReturnType<typeof _createTenantsApi>;\r\nexport const createTenantsApi: (apiContext: ApiContext) => TenantsApi = _createTenantsApi;\r\n\r\n\r\nconst _createTemplatesApi = (apiContext: ApiContext) => ({\r\n getAll: () =>\r\n apiContext.get<TenantTemplateDto[]>('/api/administration/tenants/templates'),\r\n\r\n getById: (id: string) =>\r\n apiContext.get<TenantTemplateDetailDto>(`/api/administration/tenants/templates/${id}`),\r\n\r\n create: (data: CreateTenantTemplateRequest) =>\r\n apiContext.post<TenantTemplateDto>('/api/administration/tenants/templates', data),\r\n\r\n update: (id: string, data: UpdateTenantTemplateRequest) =>\r\n apiContext.put<TenantTemplateDto>(`/api/administration/tenants/templates/${id}`, data),\r\n\r\n delete: (id: string) =>\r\n apiContext.delete(`/api/administration/tenants/templates/${id}`),\r\n\r\n assignApplication: (templateId: string, appId: string) =>\r\n apiContext.post(`/api/administration/tenants/templates/${templateId}/applications/${appId}`),\r\n\r\n removeApplication: (templateId: string, appId: string) =>\r\n apiContext.delete(`/api/administration/tenants/templates/${templateId}/applications/${appId}`),\r\n\r\n bulkAssignApplications: (templateId: string, request: BulkAssignTemplateAppsRequest) =>\r\n apiContext.post(`/api/administration/tenants/templates/${templateId}/applications/bulk`, request),\r\n\r\n getTenants: (templateId: string) =>\r\n apiContext.get<TenantBasicDto[]>(`/api/administration/tenants/templates/${templateId}/tenants`),\r\n\r\n getApplications: (templateId: string) =>\r\n apiContext.get<TemplateApplicationEnrichedDto[]>(`/api/administration/tenants/templates/${templateId}/applications`),\r\n\r\n getAvailableApplications: (templateId: string) =>\r\n apiContext.get<AvailableApplicationDto[]>(`/api/administration/tenants/templates/${templateId}/applications/available`),\r\n\r\n updateApplicationAccessLevel: (templateId: string, appId: string, accessLevel: ApplicationAccessLevel) =>\r\n apiContext.patch(`/api/administration/tenants/templates/${templateId}/applications/${appId}/access-level`, { accessLevel }),\r\n\r\n getApplicationModules: (templateId: string, appId: string) =>\r\n apiContext.get<TemplateAppModuleTreeDto[]>(`/api/administration/tenants/templates/${templateId}/applications/${appId}/modules`),\r\n\r\n updateApplicationModules: (templateId: string, appId: string, request: UpdateTemplateAppModulesRequest) =>\r\n apiContext.put(`/api/administration/tenants/templates/${templateId}/applications/${appId}/modules`, request),\r\n\r\n // AI Instances\r\n getAiInstances: (templateId: string) =>\r\n apiContext.get<TemplateAiInstanceDto[]>(`/api/administration/tenants/templates/${templateId}/ai-instances`),\r\n\r\n assignAiInstance: (templateId: string, instanceId: string) =>\r\n apiContext.post(`/api/administration/tenants/templates/${templateId}/ai-instances/${instanceId}`),\r\n\r\n removeAiInstance: (templateId: string, instanceId: string) =>\r\n apiContext.delete(`/api/administration/tenants/templates/${templateId}/ai-instances/${instanceId}`),\r\n});\r\nexport type TemplatesApi = ReturnType<typeof _createTemplatesApi>;\r\nexport const createTemplatesApi: (apiContext: ApiContext) => TemplatesApi = _createTemplatesApi;\r\n\r\n\r\nconst _createOrganisationsApi = (apiContext: ApiContext) => ({\r\n getAll: (params?: { page?: number; pageSize?: number; search?: string; countryCode?: string; verificationStatus?: VerificationStatus; sortBy?: string; sortDesc?: boolean }) =>\r\n apiContext.get<PaginatedResult<OrganisationListItemDto>>('/api/administration/tenants/organisations', { params }),\r\n\r\n getAvailable: (search?: string) =>\r\n apiContext.get<OrganisationListItemDto[]>('/api/administration/tenants/organisations/available', {\r\n params: search ? { search } : undefined\r\n }),\r\n\r\n getPendingVerification: () =>\r\n apiContext.get<OrganisationListItemDto[]>('/api/administration/tenants/organisations/pending-verification'),\r\n\r\n getById: (id: string) =>\r\n apiContext.get<OrganisationDto>(`/api/administration/tenants/organisations/${id}`),\r\n\r\n /** Like getById but returns null on 404 instead of throwing (avoids browser console noise) */\r\n findById: async (id: string): Promise<OrganisationDto | null> => {\r\n const response = await apiClient.get<OrganisationDto>(\r\n `/api/administration/tenants/organisations/${id}`,\r\n { validateStatus: (status) => status === 200 || status === 404 }\r\n );\r\n return response.status === 200 ? response.data : null;\r\n },\r\n\r\n create: (data: CreateOrganisationRequest) =>\r\n apiContext.post<OrganisationDto>('/api/administration/tenants/organisations', data),\r\n\r\n updateContact: (id: string, data: { phone?: string; email?: string; website?: string }) =>\r\n apiContext.put<OrganisationDto>(`/api/administration/tenants/organisations/${id}/contact`, data),\r\n\r\n updateAddress: (id: string, data: CreateAddressRequest) =>\r\n apiContext.put<OrganisationDto>(`/api/administration/tenants/organisations/${id}/address`, data),\r\n\r\n verify: (id: string, notes?: string) =>\r\n apiContext.post<OrganisationDto>(`/api/administration/tenants/organisations/${id}/verify`, { notes }),\r\n\r\n reject: (id: string, reason: string) =>\r\n apiContext.post<OrganisationDto>(`/api/administration/tenants/organisations/${id}/reject`, { reason }),\r\n\r\n /** Lookup company info from official registry (e.g., Zefix for Switzerland) */\r\n lookup: (countryCode: string, identifier: string) =>\r\n apiContext.get<CompanyLookupResponse>('/api/administration/tenants/organisations/lookup', {\r\n params: { countryCode, identifier }\r\n })\r\n});\r\nexport type OrganisationsApi = ReturnType<typeof _createOrganisationsApi>;\r\nexport const createOrganisationsApi: (apiContext: ApiContext) => OrganisationsApi = _createOrganisationsApi;\r\n\r\n\r\nconst _createMyTenantsApi = (apiContext: ApiContext) => ({\r\n getAll: (search?: string) =>\r\n apiContext.get<UserTenantDto[]>('/api/administration/tenants/my', {\r\n params: search ? { search } : undefined\r\n }),\r\n\r\n getDefault: () =>\r\n apiContext.get<UserTenantDto | null>('/api/administration/tenants/my/default'),\r\n\r\n setDefault: (tenantId: string) =>\r\n apiContext.post(`/api/administration/tenants/${tenantId}/set-default`),\r\n\r\n clearDefault: () =>\r\n apiContext.delete('/api/administration/tenants/clear-default'),\r\n\r\n // Favorites management (via PreferencesController with NavRoute personal.myspace.preferences)\r\n getFavorites: () =>\r\n apiContext.get<string[]>('/api/user/preferences/tenant-favorites'),\r\n\r\n addFavorite: (tenantId: string) =>\r\n apiContext.post<string[]>(`/api/user/preferences/tenant-favorites/${tenantId}`),\r\n\r\n removeFavorite: (tenantId: string) =>\r\n apiContext.delete<string[]>(`/api/user/preferences/tenant-favorites/${tenantId}`),\r\n\r\n // Recent tenants tracking\r\n recordRecent: (tenantId: string) =>\r\n apiContext.post(`/api/user/preferences/tenant-recent/${tenantId}`)\r\n});\r\nexport type MyTenantsApi = ReturnType<typeof _createMyTenantsApi>;\r\nexport const createMyTenantsApi: (apiContext: ApiContext) => MyTenantsApi = _createMyTenantsApi;\r\n\r\n","import type { ApiContext, PaginatedResult } from './_shared';\r\n\r\n// External Application Types\r\nexport interface ExternalApplicationDto {\r\n id: string;\r\n name: string;\r\n description: string | null;\r\n clientId: string;\r\n secretPrefix: string;\r\n primarySecretCreatedAt: string | null;\r\n secondarySecretPrefix: string | null;\r\n secondarySecretCreatedAt: string | null;\r\n isActive: boolean;\r\n allowedIpAddresses: string | null;\r\n tokenExpirationMinutes: number;\r\n lastAuthenticatedAt: string | null;\r\n createdAt: string;\r\n createdBy: string | null;\r\n updatedAt: string | null;\r\n updatedBy: string | null;\r\n authenticationCount: number;\r\n}\r\n\r\nexport interface ExternalApplicationDetailDto extends ExternalApplicationDto {\r\n roles: ExternalApplicationRoleDto[];\r\n}\r\n\r\nexport interface ExternalApplicationCreatedDto extends ExternalApplicationDetailDto {\r\n clientSecret: string;\r\n}\r\n\r\nexport interface RotateSecretResponse {\r\n clientSecret: string;\r\n secretPrefix: string;\r\n keySlot: string;\r\n}\r\n\r\nexport interface ExternalApplicationRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n tenantId: string | null;\r\n tenantName: string | null;\r\n isGlobal: boolean;\r\n assignedAt: string;\r\n assignedBy: string | null;\r\n}\r\n\r\nexport interface CreateExternalApplicationRequest {\r\n name: string;\r\n description?: string;\r\n allowedIpAddresses?: string;\r\n tokenExpirationMinutes?: number;\r\n}\r\n\r\nexport interface UpdateExternalApplicationRequest {\r\n name: string;\r\n description?: string;\r\n allowedIpAddresses?: string;\r\n tokenExpirationMinutes?: number;\r\n}\r\n\r\nexport interface AssignRoleToExternalAppRequest {\r\n roleId: string;\r\n tenantId?: string;\r\n}\r\n\r\nexport interface GeneratedSecretDto {\r\n clientSecret: string;\r\n}\r\n\r\nexport interface ExternalAppAuditLogDto {\r\n id: string;\r\n clientId: string;\r\n ipAddress: string;\r\n action: string;\r\n success: boolean;\r\n errorCode: string | null;\r\n errorMessage: string | null;\r\n userAgent: string | null;\r\n keyUsed: string | null;\r\n endpointCode: string | null;\r\n durationMs: number | null;\r\n createdAt: string;\r\n}\r\n\r\nexport interface ExternalAppUsageStatsDto {\r\n totalRequests: number;\r\n successfulRequests: number;\r\n failedRequests: number;\r\n topIpAddresses: IpUsageDto[];\r\n dailyUsage: DailyUsageDto[];\r\n}\r\n\r\nexport interface IpUsageDto {\r\n ipAddress: string;\r\n requestCount: number;\r\n lastSeenAt: string;\r\n}\r\n\r\nexport interface DailyUsageDto {\r\n date: string;\r\n success: number;\r\n failed: number;\r\n}\r\n\r\nexport interface KeyUsageStatsDto {\r\n primary: KeySlotStatsDto;\r\n secondary: KeySlotStatsDto | null;\r\n}\r\n\r\nexport interface KeySlotStatsDto {\r\n keySlot: string;\r\n secretPrefix: string | null;\r\n createdAt: string | null;\r\n totalUses: number;\r\n successfulUses: number;\r\n failedUses: number;\r\n lastUsedAt: string | null;\r\n lastIpAddress: string | null;\r\n topCallers: CallerStatsDto[];\r\n}\r\n\r\nexport interface CallerStatsDto {\r\n userAgent: string;\r\n requestCount: number;\r\n lastSeenAt: string;\r\n}\r\n\r\n// Data API Types\r\nexport interface DataApiEndpointDto {\r\n id: string;\r\n code: string;\r\n name: string;\r\n description: string | null;\r\n routeTemplate: string;\r\n requiredPermission: string;\r\n entityType: string;\r\n accessType: string;\r\n isActive: boolean;\r\n defaultRateLimitPerMinute: number;\r\n defaultMaxPageSize: number;\r\n navigationApplicationId: string;\r\n applicationCode: string;\r\n applicationLabel: string;\r\n navigationModuleId: string;\r\n moduleCode: string;\r\n moduleLabel: string;\r\n}\r\n\r\nexport interface ApiAccessDto {\r\n id: string;\r\n externalApplicationId: string;\r\n dataApiEndpointId: string;\r\n endpointCode: string;\r\n endpointName: string;\r\n isEnabled: boolean;\r\n rateLimitPerMinute: number | null;\r\n maxPageSize: number | null;\r\n lastAccessedAt: string | null;\r\n totalRequestCount: number;\r\n}\r\n\r\nexport interface UpdateApiAccessRequest {\r\n isEnabled: boolean;\r\n rateLimitPerMinute?: number;\r\n maxPageSize?: number;\r\n}\r\n\r\nexport interface ModuleLookupDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n}\r\n\r\nexport interface ApplicationLookupDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n modules: ModuleLookupDto[];\r\n}\r\n\r\nconst _createExternalAppsApi = (apiContext: ApiContext) => ({\r\n getAll: (params?: { search?: string; isActive?: boolean }) =>\r\n apiContext.get<ExternalApplicationDto[]>('/api/accounts', { params }),\r\n\r\n getById: (id: string) =>\r\n apiContext.get<ExternalApplicationDetailDto>(`/api/accounts/${id}`),\r\n\r\n create: (data: CreateExternalApplicationRequest) =>\r\n apiContext.post<ExternalApplicationCreatedDto>('/api/accounts', data),\r\n\r\n update: (id: string, data: UpdateExternalApplicationRequest) =>\r\n apiContext.put<ExternalApplicationDetailDto>(`/api/accounts/${id}`, data),\r\n\r\n delete: (id: string) =>\r\n apiContext.delete(`/api/accounts/${id}`),\r\n\r\n activate: (id: string) =>\r\n apiContext.patch(`/api/accounts/${id}/activate`),\r\n\r\n deactivate: (id: string) =>\r\n apiContext.patch(`/api/accounts/${id}/deactivate`),\r\n\r\n rotateSecret: (id: string, keySlot: 'Primary' | 'Secondary' = 'Primary') =>\r\n apiContext.post<RotateSecretResponse>(`/api/accounts/${id}/rotate-secret?keySlot=${keySlot}`),\r\n\r\n revokeSecondarySecret: (id: string) =>\r\n apiContext.delete(`/api/accounts/${id}/secondary-secret`),\r\n\r\n assignRole: (id: string, data: AssignRoleToExternalAppRequest) =>\r\n apiContext.post(`/api/accounts/${id}/roles`, data),\r\n\r\n removeRole: (id: string, roleId: string, tenantId?: string) =>\r\n apiContext.delete(`/api/accounts/${id}/roles/${roleId}`,\r\n { params: tenantId ? { tenantId } : undefined }),\r\n\r\n generateSecret: () =>\r\n apiContext.post<GeneratedSecretDto>('/api/accounts/generate-secret'),\r\n\r\n getAuditLogs: (id: string, page: number = 1, pageSize: number = 20) =>\r\n apiContext.get<PaginatedResult<ExternalAppAuditLogDto>>(`/api/accounts/${id}/audit-logs`, { params: { page, pageSize } }),\r\n\r\n getUsageStats: (id: string, days: number = 30) =>\r\n apiContext.get<ExternalAppUsageStatsDto>(`/api/accounts/${id}/usage-stats`, { params: { days } }),\r\n\r\n getKeyUsage: (id: string) =>\r\n apiContext.get<KeyUsageStatsDto>(`/api/accounts/${id}/key-usage`),\r\n\r\n getApiEndpoints: () =>\r\n apiContext.get<DataApiEndpointDto[]>('/api/accounts/api-endpoints'),\r\n\r\n getAppApiAccess: (appId: string) =>\r\n apiContext.get<ApiAccessDto[]>(`/api/accounts/${appId}/api-access`),\r\n\r\n updateApiAccess: (appId: string, endpointId: string, data: UpdateApiAccessRequest) =>\r\n apiContext.put<ApiAccessDto>(`/api/accounts/${appId}/api-access/${endpointId}`, data),\r\n\r\n removeApiAccess: (appId: string, endpointId: string) =>\r\n apiContext.delete(`/api/accounts/${appId}/api-access/${endpointId}`)\r\n});\r\nexport type ExternalAppsApi = ReturnType<typeof _createExternalAppsApi>;\r\nexport const createExternalAppsApi: (apiContext: ApiContext) => ExternalAppsApi = _createExternalAppsApi;\r\n\r\n\r\nconst _createApiCatalogApi = (apiContext: ApiContext) => ({\r\n getNavigationLookup: () =>\r\n apiContext.get<ApplicationLookupDto[]>('/api/apis/navigation-lookup'),\r\n});\r\nexport type ApiCatalogApi = ReturnType<typeof _createApiCatalogApi>;\r\nexport const createApiCatalogApi: (apiContext: ApiContext) => ApiCatalogApi = _createApiCatalogApi;\r\n\r\n","import { api, withTenantContext, type ApiContext } from './_shared';\r\nimport { createUsersApi } from './users';\r\nimport { createRolesApi, createPermissionsApi } from './roles';\r\nimport { createApplicationsApi } from './applications';\r\nimport { createDashboardApi } from './dashboard';\r\nimport { createSettingsApi, createPreferencesApi } from './settings';\r\nimport { createTenantsApi, createTemplatesApi, createOrganisationsApi, createMyTenantsApi } from './tenants';\r\nimport { createExternalAppsApi, createApiCatalogApi } from './externalApps';\r\n\r\n// Re-export ALL types from all domain files\r\nexport * from './users';\r\nexport * from './roles';\r\nexport * from './applications';\r\nexport * from './dashboard';\r\nexport * from './settings';\r\nexport * from './tenants';\r\nexport * from './externalApps';\r\nexport * from './crossCutting';\r\n\r\n// Re-export shared types\r\nexport type { ApiContext } from './_shared';\r\nexport type { PaginatedResult } from './_shared';\r\n\r\n/**\r\n * Creates the Admin API service with an optional tenant context.\r\n * When tenantSlug is provided, all API calls will include the X-Tenant-Slug header.\r\n * This is needed for Super Admins viewing tenant-specific pages without \"switching\" to that tenant.\r\n */\r\nconst createAdminApi = (tenantSlug?: string) => {\r\n const apiContext: ApiContext = tenantSlug ? withTenantContext(tenantSlug) : api;\r\n\r\n return {\r\n users: createUsersApi(apiContext),\r\n roles: createRolesApi(apiContext),\r\n permissions: createPermissionsApi(apiContext),\r\n applications: createApplicationsApi(apiContext),\r\n dashboard: createDashboardApi(apiContext),\r\n settings: createSettingsApi(apiContext),\r\n tenants: createTenantsApi(apiContext),\r\n templates: createTemplatesApi(apiContext),\r\n organisations: createOrganisationsApi(apiContext),\r\n myTenants: createMyTenantsApi(apiContext),\r\n preferences: createPreferencesApi(apiContext),\r\n externalApps: createExternalAppsApi(apiContext),\r\n apiCatalog: createApiCatalogApi(apiContext),\r\n };\r\n};\r\n\r\n// Default export uses localStorage tenant slug (backward compatible)\r\nexport const adminApi = createAdminApi();\r\n\r\n// Export factory for explicit tenant context\r\nexport { createAdminApi };\r\n","import i18n from 'i18next';\r\nimport { initReactI18next } from 'react-i18next';\r\nimport LanguageDetector from 'i18next-browser-languagedetector';\r\n\r\n// Lazy load function for all languages (loaded dynamically to avoid bundler warnings)\r\nconst loadLanguageResources = async (lang: string) => {\r\n if (!lang) return null;\r\n\r\n try {\r\n const [\r\n common,\r\n navigation,\r\n auth,\r\n admin,\r\n support,\r\n docs,\r\n ai,\r\n communications,\r\n entra,\r\n structure,\r\n preferences,\r\n groups,\r\n ba,\r\n docsAdministrationUsers,\r\n docsAdministrationPermissions,\r\n docsAdministrationTenants,\r\n docsAdministrationAi,\r\n docsAdministrationWorkflows,\r\n docsAdministrationEntra,\r\n docsAdministrationApplications,\r\n docsAdministrationConfiguration,\r\n docsAdministrationConfigurationLicense,\r\n docsAdministrationTenantsTemplate,\r\n docsAdministrationTenantsBusiness,\r\n docsAdministrationTenantsPersonal,\r\n docsAdministrationTenantsOther,\r\n docsAdministrationPermissionsAssignments,\r\n docsAdministrationTenantsApplications,\r\n docsAdministrationTenantsAccessRequests,\r\n docsAdministrationTenantsOrganisations,\r\n docsPlatformSupportTickets,\r\n docsAdministrationUsersDashboard,\r\n docsAdministrationUsersReferences,\r\n docsAdministrationUsersGroups\r\n ] = await Promise.all([\r\n import(`./locales/${lang}/common.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/navigation.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/auth.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/admin.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/support.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/ai.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/communications.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/entra.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/structure.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/preferences.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/groups.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/ba.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-users.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-permissions.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-tenants.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-ai.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-workflows.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-entra.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-applications.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-configuration.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-configuration-license.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-tenants-template.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-tenants-business.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-tenants-personal.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-tenants-other.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-permissions-assignments.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-tenants-applications.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-tenants-access-requests.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-tenants-organisations.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-platform-support-tickets.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-users-dashboard.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-users-references.json`).catch(() => ({ default: {} })),\r\n import(`./locales/${lang}/docs-administration-users-groups.json`).catch(() => ({ default: {} }))\r\n ]);\r\n\r\n return {\r\n common: common.default || {},\r\n navigation: navigation.default || {},\r\n auth: auth.default || {},\r\n admin: admin.default || {},\r\n support: support.default || {},\r\n docs: docs.default || {},\r\n ai: ai.default || {},\r\n communications: communications.default || {},\r\n entra: entra.default || {},\r\n structure: structure.default || {},\r\n preferences: preferences.default || {},\r\n groups: groups.default || {},\r\n ba: ba.default || {},\r\n docsAdministrationUsers: docsAdministrationUsers.default || {},\r\n docsAdministrationPermissions: docsAdministrationPermissions.default || {},\r\n docsAdministrationTenants: docsAdministrationTenants.default || {},\r\n docsAdministrationAi: docsAdministrationAi.default || {},\r\n docsAdministrationWorkflows: docsAdministrationWorkflows.default || {},\r\n docsAdministrationEntra: docsAdministrationEntra.default || {},\r\n docsAdministrationApplications: docsAdministrationApplications.default || {},\r\n docsAdministrationConfiguration: docsAdministrationConfiguration.default || {},\r\n docsAdministrationConfigurationLicense: docsAdministrationConfigurationLicense.default || {},\r\n docsAdministrationTenantsTemplate: docsAdministrationTenantsTemplate.default || {},\r\n docsAdministrationTenantsBusiness: docsAdministrationTenantsBusiness.default || {},\r\n docsAdministrationTenantsPersonal: docsAdministrationTenantsPersonal.default || {},\r\n docsAdministrationTenantsOther: docsAdministrationTenantsOther.default || {},\r\n docsAdministrationPermissionsAssignments: docsAdministrationPermissionsAssignments.default || {},\r\n docsAdministrationTenantsApplications: docsAdministrationTenantsApplications.default || {},\r\n docsAdministrationTenantsAccessRequests: docsAdministrationTenantsAccessRequests.default || {},\r\n docsAdministrationTenantsOrganisations: docsAdministrationTenantsOrganisations.default || {},\r\n docsPlatformSupportTickets: docsPlatformSupportTickets.default || {},\r\n docsAdministrationUsersDashboard: docsAdministrationUsersDashboard.default || {},\r\n docsAdministrationUsersReferences: docsAdministrationUsersReferences.default || {},\r\n docsAdministrationUsersGroups: docsAdministrationUsersGroups.default || {}\r\n };\r\n } catch (error) {\r\n console.warn(`Failed to load language: ${lang}`, error);\r\n return null;\r\n }\r\n};\r\n\r\n// Start with empty resources (all languages loaded dynamically)\r\nconst resources = {};\r\n\r\n// Detect user's preferred language\r\nconst detectLanguage = (): string => {\r\n // Check localStorage first\r\n const stored = localStorage.getItem('i18nextLng');\r\n if (stored && ['en', 'fr', 'de', 'it'].includes(stored)) {\r\n return stored;\r\n }\r\n\r\n // Check browser language\r\n const browserLang = navigator.language.split('-')[0];\r\n if (['en', 'fr', 'de', 'it'].includes(browserLang)) {\r\n return browserLang;\r\n }\r\n\r\n return 'en';\r\n};\r\n\r\nconst detectedLang = detectLanguage();\r\n\r\n// Initialize i18n\r\ni18n\r\n .use(LanguageDetector)\r\n .use(initReactI18next)\r\n .init({\r\n resources,\r\n lng: detectedLang, // Use detected language\r\n fallbackLng: 'en',\r\n defaultNS: 'common',\r\n ns: ['common', 'navigation', 'auth', 'admin', 'support', 'docs', 'ai', 'communications', 'entra', 'structure', 'preferences', 'groups', 'ba', 'docsAdministrationUsers', 'docsAdministrationPermissions', 'docsAdministrationTenants', 'docsAdministrationAi', 'docsAdministrationWorkflows', 'docsAdministrationEntra', 'docsAdministrationApplications', 'docsAdministrationConfiguration', 'docsAdministrationConfigurationLicense', 'docsAdministrationTenantsTemplate', 'docsAdministrationTenantsBusiness', 'docsAdministrationTenantsPersonal', 'docsAdministrationTenantsOther', 'docsAdministrationPermissionsAssignments', 'docsAdministrationTenantsApplications', 'docsAdministrationTenantsAccessRequests', 'docsAdministrationTenantsOrganisations', 'docsPlatformSupportTickets', 'docsAdministrationUsersReferences', 'docsAdministrationUsersGroups'],\r\n\r\n interpolation: {\r\n escapeValue: false,\r\n },\r\n\r\n detection: {\r\n order: ['localStorage', 'navigator', 'htmlTag'],\r\n caches: ['localStorage'],\r\n },\r\n });\r\n\r\n// Helper to add resource bundles for a language\r\nconst addResources = (lang: string, langResources: Record<string, unknown>) => {\r\n Object.entries(langResources).forEach(([ns, translations]) => {\r\n i18n.addResourceBundle(lang, ns, translations as Record<string, unknown>, true, true);\r\n });\r\n};\r\n\r\n// Load the detected language AND the fallback language on initialization\r\nconst initPromise = (async () => {\r\n const loads: Promise<void>[] = [];\r\n\r\n // Always load the fallback language (English) first so keys are never raw\r\n if (detectedLang !== 'en') {\r\n loads.push(\r\n loadLanguageResources('en').then(res => {\r\n if (res) addResources('en', res);\r\n })\r\n );\r\n }\r\n\r\n // Load the detected language\r\n loads.push(\r\n loadLanguageResources(detectedLang).then(res => {\r\n if (res) addResources(detectedLang, res);\r\n })\r\n );\r\n\r\n await Promise.all(loads);\r\n})();\r\n\r\n// Exported promise so the app can await i18n readiness before first render\r\nexport const i18nReady = initPromise;\r\n\r\n// Export function to change language (loads resources on demand)\r\nexport const changeLanguage = async (lang: string): Promise<void> => {\r\n // Ensure init has completed before switching\r\n await initPromise;\r\n\r\n if (!i18n.hasResourceBundle(lang, 'common')) {\r\n const langResources = await loadLanguageResources(lang);\r\n if (langResources) {\r\n addResources(lang, langResources);\r\n }\r\n }\r\n await i18n.changeLanguage(lang);\r\n};\r\n\r\nexport default i18n;\r\n","import { createContext, useContext, useEffect, useLayoutEffect, useState, useCallback, useRef, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport type { ThemeMode, ThemeConfig, BorderRadiusConfig, BorderRadiusSize, ItemPaletteKey } from '@/types/theme';\r\nimport { BORDER_RADIUS_VALUES } from '@/types/theme';\r\nimport {\r\n DEFAULT_THEME_CONFIG,\r\n THEME_STORAGE_KEY,\r\n createLightPalette,\r\n createDarkPalette,\r\n ACCENT_COLORS,\r\n ITEM_PALETTES,\r\n DEFAULT_ITEM_PALETTE,\r\n} from '@/config/themePresets';\r\nimport { userApi, type UserPreferencesDto } from '@/services/api/userApi';\r\nimport { adminApi, type PreferencesScopeInfo } from '@/services/api/adminApi';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { changeLanguage } from '@/i18n/config';\r\n\r\ninterface ThemeContextType {\r\n config: ThemeConfig;\r\n mode: ThemeMode;\r\n toggleMode: () => void;\r\n setMode: (mode: ThemeMode) => void;\r\n setBorderRadius: (element: keyof BorderRadiusConfig, size: BorderRadiusSize) => void;\r\n setAccentColor: (colorKey: string) => void;\r\n setItemPalette: (paletteKey: ItemPaletteKey) => void;\r\n applyPreset: (presetConfig: Partial<ThemeConfig>) => void;\r\n resetToDefault: () => void;\r\n getBorderRadius: (element: keyof BorderRadiusConfig) => string;\r\n /** Load preferences from server. Pass 'global' to load only global prefs, 'tenant' for effective (tenant with fallback). */\r\n loadFromServer: (forceScope?: 'global' | 'tenant' | null) => Promise<void>;\r\n isSyncing: boolean;\r\n // Scope management\r\n scopeInfo: PreferencesScopeInfo | null;\r\n applyToTenant: () => Promise<void>;\r\n resetToGlobal: () => Promise<void>;\r\n // Tenant-specific theme editing\r\n selectedThemeTenant: string | null | undefined; // undefined = not initialized, null = global, string = tenant ID\r\n /** Set the tenant scope for saving. skipLoad=true to just set scope without loading preferences. */\r\n setSelectedThemeTenant: (tenantId: string | null, skipLoad?: boolean) => Promise<void>;\r\n tenantOverrides: string[]; // List of tenant IDs that have overrides\r\n loadTenantOverrides: () => Promise<void>;\r\n}\r\n\r\nconst ThemeContext = createContext<ThemeContextType | undefined>(undefined);\r\n\r\nfunction loadThemeConfig(): ThemeConfig {\r\n if (typeof window === 'undefined') return DEFAULT_THEME_CONFIG;\r\n\r\n try {\r\n const saved = localStorage.getItem(THEME_STORAGE_KEY);\r\n if (saved) {\r\n const parsed = JSON.parse(saved) as Partial<ThemeConfig>;\r\n return {\r\n ...DEFAULT_THEME_CONFIG,\r\n ...parsed,\r\n borderRadius: { ...DEFAULT_THEME_CONFIG.borderRadius, ...parsed.borderRadius },\r\n itemPaletteKey: parsed.itemPaletteKey || DEFAULT_ITEM_PALETTE,\r\n colors: {\r\n light: createLightPalette(parsed.accentColorKey || 'indigo'),\r\n dark: createDarkPalette(parsed.accentColorKey || 'indigo'),\r\n },\r\n };\r\n }\r\n } catch {\r\n }\r\n\r\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\r\n return { ...DEFAULT_THEME_CONFIG, mode: prefersDark ? 'dark' : 'light' };\r\n}\r\n\r\nfunction serverPrefsToConfig(prefs: UserPreferencesDto): ThemeConfig {\r\n const accentColorKey = prefs.accentColorKey || 'indigo';\r\n const br = prefs.borderRadius;\r\n return {\r\n mode: (prefs.theme as ThemeMode) || 'dark',\r\n accentColorKey,\r\n borderRadius: {\r\n card: (br?.card as BorderRadiusSize) || 'large',\r\n button: (br?.button as BorderRadiusSize) || 'medium',\r\n badge: (br?.badge as BorderRadiusSize) || 'medium',\r\n input: (br?.input as BorderRadiusSize) || 'medium',\r\n modal: (br?.modal as BorderRadiusSize) || 'xlarge',\r\n menuItem: (br?.menuItem as BorderRadiusSize) || 'medium',\r\n },\r\n itemPaletteKey: (prefs.itemPaletteKey as ItemPaletteKey) || DEFAULT_ITEM_PALETTE,\r\n colors: {\r\n light: createLightPalette(accentColorKey),\r\n dark: createDarkPalette(accentColorKey),\r\n },\r\n };\r\n}\r\n\r\nfunction applyThemeToDocument(config: ThemeConfig) {\r\n const root = document.documentElement;\r\n const palette = config.mode === 'dark' ? config.colors.dark : config.colors.light;\r\n const accent = ACCENT_COLORS[config.accentColorKey]?.shades || ACCENT_COLORS.indigo.shades;\r\n\r\n // Get item palette colors\r\n const itemPalette = ITEM_PALETTES[config.itemPaletteKey] || ITEM_PALETTES.neutral;\r\n const itemColors = config.mode === 'dark' ? itemPalette.colors.dark : itemPalette.colors.light;\r\n\r\n if (config.mode === 'dark') {\r\n root.classList.add('dark');\r\n } else {\r\n root.classList.remove('dark');\r\n }\r\n\r\n root.style.setProperty('--radius-card', BORDER_RADIUS_VALUES[config.borderRadius.card]);\r\n root.style.setProperty('--radius-button', BORDER_RADIUS_VALUES[config.borderRadius.button]);\r\n root.style.setProperty('--radius-badge', BORDER_RADIUS_VALUES[config.borderRadius.badge]);\r\n root.style.setProperty('--radius-input', BORDER_RADIUS_VALUES[config.borderRadius.input]);\r\n root.style.setProperty('--radius-modal', BORDER_RADIUS_VALUES[config.borderRadius.modal]);\r\n root.style.setProperty('--radius-menu-item', BORDER_RADIUS_VALUES[config.borderRadius.menuItem]);\r\n\r\n Object.entries(accent).forEach(([key, value]) => {\r\n root.style.setProperty(`--color-accent-${key}`, value);\r\n });\r\n\r\n // Apply item palette colors for gradient backgrounds\r\n root.style.setProperty('--item-color-light', itemColors.light);\r\n root.style.setProperty('--item-color-medium', itemColors.medium);\r\n root.style.setProperty('--item-color-dark', itemColors.dark);\r\n root.style.setProperty('--item-color-border', itemColors.border);\r\n\r\n root.style.setProperty('--bg-app', palette.backgrounds.app);\r\n root.style.setProperty('--bg-primary', palette.backgrounds.primary);\r\n root.style.setProperty('--bg-secondary', palette.backgrounds.secondary);\r\n root.style.setProperty('--bg-tertiary', palette.backgrounds.tertiary);\r\n root.style.setProperty('--bg-card', palette.backgrounds.card);\r\n root.style.setProperty('--bg-hover', palette.backgrounds.hover);\r\n root.style.setProperty('--bg-active', palette.backgrounds.active);\r\n\r\n root.style.setProperty('--text-primary', palette.text.primary);\r\n root.style.setProperty('--text-secondary', palette.text.secondary);\r\n root.style.setProperty('--text-tertiary', palette.text.tertiary);\r\n root.style.setProperty('--text-muted', palette.text.muted);\r\n root.style.setProperty('--text-inverse', palette.text.inverse);\r\n\r\n root.style.setProperty('--border-color', palette.border.default);\r\n root.style.setProperty('--border-subtle', palette.border.subtle);\r\n root.style.setProperty('--border-strong', palette.border.strong);\r\n\r\n root.style.setProperty('--success-bg', palette.status.success.bg);\r\n root.style.setProperty('--success-text', palette.status.success.text);\r\n root.style.setProperty('--success-border', palette.status.success.border);\r\n root.style.setProperty('--success-dot', palette.status.success.dot);\r\n\r\n root.style.setProperty('--warning-bg', palette.status.warning.bg);\r\n root.style.setProperty('--warning-text', palette.status.warning.text);\r\n root.style.setProperty('--warning-border', palette.status.warning.border);\r\n root.style.setProperty('--warning-dot', palette.status.warning.dot);\r\n\r\n root.style.setProperty('--error-bg', palette.status.error.bg);\r\n root.style.setProperty('--error-text', palette.status.error.text);\r\n root.style.setProperty('--error-border', palette.status.error.border);\r\n root.style.setProperty('--error-dot', palette.status.error.dot);\r\n\r\n root.style.setProperty('--info-bg', palette.status.info.bg);\r\n root.style.setProperty('--info-text', palette.status.info.text);\r\n root.style.setProperty('--info-border', palette.status.info.border);\r\n root.style.setProperty('--info-dot', palette.status.info.dot);\r\n\r\n root.style.setProperty('--accent-bg', palette.accentTransparent.bg);\r\n root.style.setProperty('--accent-text', palette.accentTransparent.text);\r\n root.style.setProperty('--accent-border', palette.accentTransparent.border);\r\n}\r\n\r\nexport function ThemeProvider({ children }: { children: ReactNode }): ReactElement {\r\n const [config, setConfig] = useState<ThemeConfig>(loadThemeConfig);\r\n const [isSyncing, setIsSyncing] = useState(false);\r\n const [scopeInfo, setScopeInfo] = useState<PreferencesScopeInfo | null>(null);\r\n // undefined = not yet initialized (should use current tenant from localStorage)\r\n // null = explicitly global (user chose \"Global\" in scope selector)\r\n // string = specific tenant ID\r\n const [selectedThemeTenant, setSelectedThemeTenantState] = useState<string | null | undefined>(undefined);\r\n const [tenantOverrides, setTenantOverrides] = useState<string[]>([]);\r\n const { i18n } = useTranslation();\r\n const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const isInitialLoadRef = useRef(true);\r\n // Track if theme scope has been explicitly set (by ThemeSync or user)\r\n // When false, saves will use current tenant from localStorage as fallback\r\n const isThemeScopeInitializedRef = useRef(false);\r\n // Track if we're in the middle of a tenant switch to block saves during transitions\r\n const isSwitchingTenantRef = useRef(false);\r\n\r\n const saveToServer = useCallback(async (newConfig: ThemeConfig, targetTenantId: string | null | undefined = undefined) => {\r\n const token = localStorage.getItem('token');\r\n\r\n // Skip server sync if not authenticated\r\n if (!token) {\r\n return;\r\n }\r\n\r\n // Skip if tenant switch is in progress (debounced save from before switch)\r\n if (isSwitchingTenantRef.current) {\r\n return;\r\n }\r\n\r\n // Determine the correct tenant to save to:\r\n // - undefined = not yet initialized, use current tenant from localStorage OR save to global\r\n // - null = explicitly global (user chose \"Global\" in scope selector)\r\n // - string = specific tenant ID\r\n let effectiveTargetTenantId: string | null = null;\r\n const currentTenantIdInStorage = localStorage.getItem('currentTenantId');\r\n\r\n if (targetTenantId === undefined) {\r\n // Not initialized yet - try current tenant, otherwise save to global\r\n if (currentTenantIdInStorage) {\r\n effectiveTargetTenantId = currentTenantIdInStorage;\r\n } else {\r\n effectiveTargetTenantId = null;\r\n }\r\n } else {\r\n // Explicitly set (null for global, string for tenant)\r\n // Safety check: if saving to a specific tenant, verify it matches current context\r\n // This prevents saving to wrong tenant after a quick tenant switch\r\n if (targetTenantId !== null && currentTenantIdInStorage && targetTenantId !== currentTenantIdInStorage) {\r\n return;\r\n }\r\n effectiveTargetTenantId = targetTenantId;\r\n }\r\n\r\n try {\r\n setIsSyncing(true);\r\n await userApi.preferences.updateTheme({\r\n mode: newConfig.mode,\r\n accentColorKey: newConfig.accentColorKey,\r\n borderRadius: {\r\n card: newConfig.borderRadius.card,\r\n button: newConfig.borderRadius.button,\r\n badge: newConfig.borderRadius.badge,\r\n input: newConfig.borderRadius.input,\r\n modal: newConfig.borderRadius.modal,\r\n menuItem: newConfig.borderRadius.menuItem,\r\n },\r\n itemPaletteKey: newConfig.itemPaletteKey,\r\n targetTenantId: effectiveTargetTenantId,\r\n });\r\n\r\n // If saving to a specific tenant, update the overrides list\r\n if (effectiveTargetTenantId && !tenantOverrides.includes(effectiveTargetTenantId)) {\r\n setTenantOverrides(prev => [...prev, effectiveTargetTenantId]);\r\n }\r\n } catch (error) {\r\n console.error('[ThemeContext] ❌ Failed to save theme to server:', {\r\n error,\r\n targetTenantId: effectiveTargetTenantId,\r\n mode: newConfig.mode,\r\n accentColorKey: newConfig.accentColorKey,\r\n localStorage_currentTenantId: localStorage.getItem('currentTenantId'),\r\n localStorage_currentTenantSlug: localStorage.getItem('currentTenantSlug'),\r\n });\r\n } finally {\r\n setIsSyncing(false);\r\n }\r\n }, [tenantOverrides]);\r\n\r\n const debouncedSaveToServer = useCallback((newConfig: ThemeConfig, targetTenantId: string | null | undefined = undefined) => {\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n saveTimeoutRef.current = setTimeout(() => {\r\n saveToServer(newConfig, targetTenantId);\r\n }, 1000);\r\n }, [saveToServer]);\r\n\r\n useLayoutEffect(() => {\r\n applyThemeToDocument(config);\r\n }, [config]);\r\n\r\n useEffect(() => {\r\n const toSave = {\r\n mode: config.mode,\r\n borderRadius: config.borderRadius,\r\n accentColorKey: config.accentColorKey,\r\n itemPaletteKey: config.itemPaletteKey,\r\n };\r\n localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify(toSave));\r\n\r\n // Block saves during tenant transitions or initial load\r\n if (!isSwitchingTenantRef.current && !isInitialLoadRef.current) {\r\n // Save to the selected tenant (null = global)\r\n debouncedSaveToServer(config, selectedThemeTenant);\r\n }\r\n isInitialLoadRef.current = false;\r\n }, [config, debouncedSaveToServer, selectedThemeTenant]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n };\r\n }, []);\r\n\r\n const loadFromServer = useCallback(async (forceScope?: 'global' | 'tenant' | null) => {\r\n const token = localStorage.getItem('token');\r\n const tenantSlug = localStorage.getItem('currentTenantSlug');\r\n\r\n if (!token) {\r\n return;\r\n }\r\n\r\n // Determine which scope to load:\r\n // - forceScope: 'global' -> load global only (no tenant slug required)\r\n // - forceScope: 'tenant' -> load effective (tenant with fallback to global, requires tenant slug)\r\n // - forceScope: null/undefined -> load effective (default behavior, requires tenant slug)\r\n const loadGlobalOnly = forceScope === 'global';\r\n\r\n // Only require tenant slug if loading tenant-specific or effective preferences\r\n if (!loadGlobalOnly && !tenantSlug) {\r\n return;\r\n }\r\n\r\n try {\r\n setIsSyncing(true);\r\n\r\n // Use the appropriate endpoint based on scope\r\n const prefs = loadGlobalOnly\r\n ? await adminApi.preferences.getGlobal()\r\n : await adminApi.preferences.get();\r\n\r\n // Store scope info\r\n setScopeInfo(prefs.scope);\r\n\r\n const serverConfig = serverPrefsToConfig(prefs);\r\n\r\n isInitialLoadRef.current = true;\r\n setConfig(serverConfig);\r\n // Mark scope as initialized since we've loaded from server with valid scope info\r\n isThemeScopeInitializedRef.current = true;\r\n\r\n // Also apply language preference (lazy loads translations if needed)\r\n if (prefs.language && prefs.language !== i18n.language) {\r\n await changeLanguage(prefs.language);\r\n document.documentElement.lang = prefs.language;\r\n }\r\n } catch (error) {\r\n console.error('[ThemeContext] Failed to load theme from server:', error);\r\n } finally {\r\n setIsSyncing(false);\r\n }\r\n }, [i18n]);\r\n\r\n const applyToTenant = useCallback(async () => {\r\n const token = localStorage.getItem('token');\r\n const tenantSlug = localStorage.getItem('currentTenantSlug');\r\n\r\n if (!token || !tenantSlug) {\r\n return;\r\n }\r\n\r\n try {\r\n setIsSyncing(true);\r\n const result = await adminApi.preferences.applyToTenant();\r\n setScopeInfo(result.scope);\r\n } catch (error) {\r\n console.error('[ThemeContext] Failed to create tenant-specific preferences:', error);\r\n throw error;\r\n } finally {\r\n setIsSyncing(false);\r\n }\r\n }, []);\r\n\r\n const resetToGlobal = useCallback(async () => {\r\n const token = localStorage.getItem('token');\r\n const tenantSlug = localStorage.getItem('currentTenantSlug');\r\n\r\n if (!token || !tenantSlug) {\r\n return;\r\n }\r\n\r\n try {\r\n setIsSyncing(true);\r\n const result = await adminApi.preferences.deleteTenantOverride();\r\n\r\n // Update scope info\r\n setScopeInfo(result.scope);\r\n\r\n // Apply the returned preferences (global or system defaults)\r\n const serverConfig = serverPrefsToConfig(result);\r\n isInitialLoadRef.current = true;\r\n setConfig(serverConfig);\r\n } catch (error) {\r\n console.error('[ThemeContext] Failed to delete tenant-specific preferences:', error);\r\n throw error;\r\n } finally {\r\n setIsSyncing(false);\r\n }\r\n }, []);\r\n\r\n // Load list of tenants that have theme overrides\r\n const loadTenantOverrides = useCallback(async () => {\r\n const token = localStorage.getItem('token');\r\n if (!token) return;\r\n\r\n try {\r\n const overrides = await adminApi.preferences.getThemeOverrides();\r\n setTenantOverrides(overrides);\r\n } catch (error) {\r\n console.error('[ThemeContext] Failed to load tenant overrides:', error);\r\n }\r\n }, []);\r\n\r\n // Set selected tenant for saving. If skipLoad is false, also load that tenant's preferences.\r\n // skipLoad=true is used by ThemeSync to set the save target without overwriting user's changes.\r\n // skipLoad=false (default) is used by ThemeScopeSelector when user explicitly switches scope.\r\n const setSelectedThemeTenant = useCallback(async (tenantId: string | null, skipLoad: boolean = false) => {\r\n // CRITICAL: Cancel any pending debounced save from the previous tenant\r\n // This prevents saving to the wrong tenant when user switches quickly\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n saveTimeoutRef.current = null;\r\n }\r\n\r\n // Mark that we're switching tenants - blocks saves during the transition\r\n isSwitchingTenantRef.current = true;\r\n // Mark scope as initialized - user or ThemeSync has explicitly set it\r\n isThemeScopeInitializedRef.current = true;\r\n // CRITICAL: Set isInitialLoadRef BEFORE changing state to prevent the useEffect\r\n // from triggering a save while we're in the middle of switching tenants\r\n isInitialLoadRef.current = true;\r\n setSelectedThemeTenantState(tenantId);\r\n\r\n // If skipLoad is true, just set the scope without loading preferences\r\n // This prevents overwriting the user's current theme changes\r\n if (skipLoad) {\r\n isSwitchingTenantRef.current = false;\r\n return;\r\n }\r\n\r\n const token = localStorage.getItem('token');\r\n if (!token) {\r\n isSwitchingTenantRef.current = false;\r\n return;\r\n }\r\n\r\n try {\r\n setIsSyncing(true);\r\n\r\n if (tenantId === null) {\r\n // Load ONLY global preferences (TenantId = null), not effective preferences\r\n // This ensures editing global scope shows the actual global values\r\n const prefs = await adminApi.preferences.getGlobal();\r\n setScopeInfo(prefs.scope);\r\n const serverConfig = serverPrefsToConfig(prefs);\r\n isInitialLoadRef.current = true;\r\n setConfig(serverConfig);\r\n } else {\r\n // Load tenant-specific preferences (with fallback to global)\r\n const prefs = await adminApi.preferences.getForTenant(tenantId);\r\n setScopeInfo(prefs.scope);\r\n const serverConfig = serverPrefsToConfig(prefs);\r\n isInitialLoadRef.current = true;\r\n setConfig(serverConfig);\r\n }\r\n } catch (error) {\r\n console.error('[ThemeContext] Failed to load preferences for tenant:', error);\r\n } finally {\r\n setIsSyncing(false);\r\n // End of tenant switch - saves can now proceed normally\r\n isSwitchingTenantRef.current = false;\r\n }\r\n }, []);\r\n\r\n const toggleMode = useCallback(() => {\r\n setConfig(prev => ({\r\n ...prev,\r\n mode: prev.mode === 'light' ? 'dark' : 'light',\r\n colors: {\r\n light: createLightPalette(prev.accentColorKey),\r\n dark: createDarkPalette(prev.accentColorKey),\r\n },\r\n }));\r\n }, []);\r\n\r\n const setMode = useCallback((mode: ThemeMode) => {\r\n setConfig(prev => ({ ...prev, mode }));\r\n }, []);\r\n\r\n const setBorderRadius = useCallback((element: keyof BorderRadiusConfig, size: BorderRadiusSize) => {\r\n setConfig(prev => ({\r\n ...prev,\r\n borderRadius: { ...prev.borderRadius, [element]: size },\r\n }));\r\n }, []);\r\n\r\n const setAccentColor = useCallback((colorKey: string) => {\r\n if (!ACCENT_COLORS[colorKey]) return;\r\n setConfig(prev => ({\r\n ...prev,\r\n accentColorKey: colorKey,\r\n colors: {\r\n light: createLightPalette(colorKey),\r\n dark: createDarkPalette(colorKey),\r\n },\r\n }));\r\n }, []);\r\n\r\n const setItemPalette = useCallback((paletteKey: ItemPaletteKey) => {\r\n if (!ITEM_PALETTES[paletteKey]) return;\r\n setConfig(prev => ({\r\n ...prev,\r\n itemPaletteKey: paletteKey,\r\n }));\r\n }, []);\r\n\r\n const applyPreset = useCallback((presetConfig: Partial<ThemeConfig>) => {\r\n setConfig(prev => {\r\n const newAccent = presetConfig.accentColorKey || prev.accentColorKey;\r\n return {\r\n ...prev,\r\n ...presetConfig,\r\n borderRadius: { ...prev.borderRadius, ...presetConfig.borderRadius },\r\n accentColorKey: newAccent,\r\n itemPaletteKey: presetConfig.itemPaletteKey || prev.itemPaletteKey,\r\n colors: {\r\n light: createLightPalette(newAccent),\r\n dark: createDarkPalette(newAccent),\r\n },\r\n };\r\n });\r\n }, []);\r\n\r\n const resetToDefault = useCallback(() => {\r\n setConfig(DEFAULT_THEME_CONFIG);\r\n }, []);\r\n\r\n const getBorderRadius = useCallback((element: keyof BorderRadiusConfig) => {\r\n return BORDER_RADIUS_VALUES[config.borderRadius[element]];\r\n }, [config.borderRadius]);\r\n\r\n return (\r\n <ThemeContext.Provider value={{\r\n config,\r\n mode: config.mode,\r\n toggleMode,\r\n setMode,\r\n setBorderRadius,\r\n setAccentColor,\r\n setItemPalette,\r\n applyPreset,\r\n resetToDefault,\r\n getBorderRadius,\r\n loadFromServer,\r\n isSyncing,\r\n // Scope management\r\n scopeInfo,\r\n applyToTenant,\r\n resetToGlobal,\r\n // Tenant-specific theme editing\r\n selectedThemeTenant,\r\n setSelectedThemeTenant,\r\n tenantOverrides,\r\n loadTenantOverrides,\r\n }}>\r\n {children}\r\n </ThemeContext.Provider>\r\n );\r\n}\r\n\r\nexport function useTheme(): ThemeContextType {\r\n const context = useContext(ThemeContext);\r\n if (context === undefined) {\r\n throw new Error('useTheme must be used within a ThemeProvider');\r\n }\r\n return context;\r\n}\r\n\r\nexport function useThemeMode(): { theme: ThemeMode; toggleTheme: () => void; setTheme: (mode: ThemeMode) => void } {\r\n const { mode, toggleMode, setMode } = useTheme();\r\n return { theme: mode, toggleTheme: toggleMode, setTheme: setMode };\r\n}\r\n","import axios from 'axios';\r\n\r\nexport interface FeatureConfig {\r\n multiTenantEnabled: boolean;\r\n enableB2B: boolean;\r\n enableB2C: boolean;\r\n useUrlRouting: boolean;\r\n}\r\n\r\nlet cachedConfig: FeatureConfig | null = null;\r\nlet pendingRequest: Promise<FeatureConfig> | null = null;\r\n\r\n/**\r\n * API service for application configuration.\r\n * Uses a separate axios instance (no auth required) to avoid circular dependencies\r\n * with the main apiClient which depends on tenant context.\r\n */\r\nexport const configApi = {\r\n /**\r\n * Gets feature configuration from the backend.\r\n * Result is cached for the lifetime of the page.\r\n * Prevents duplicate concurrent requests (StrictMode protection).\r\n */\r\n async getFeatureConfig(signal?: AbortSignal): Promise<FeatureConfig> {\r\n // Return cached config if available\r\n if (cachedConfig) return cachedConfig;\r\n\r\n // If a request is already in progress, wait for it (deduplication)\r\n if (pendingRequest) return pendingRequest;\r\n\r\n // Create new request and track it\r\n pendingRequest = (async () => {\r\n try {\r\n const response = await axios.get<FeatureConfig>('/api/config/features', {\r\n timeout: 10000,\r\n signal,\r\n headers: {\r\n 'Content-Type': 'application/json'\r\n }\r\n });\r\n\r\n cachedConfig = response.data;\r\n return cachedConfig;\r\n } finally {\r\n pendingRequest = null;\r\n }\r\n })();\r\n\r\n return pendingRequest;\r\n },\r\n\r\n /**\r\n * Clears the cached configuration.\r\n * Call this if you need to force a refresh (e.g., after config change).\r\n */\r\n clearCache() {\r\n cachedConfig = null;\r\n pendingRequest = null;\r\n }\r\n};\r\n","import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { configApi, type FeatureConfig } from '@/services/api/configApi';\r\n\r\ninterface FeatureConfigContextType {\r\n /** The raw feature configuration from the backend */\r\n config: FeatureConfig | null;\r\n /** Whether the configuration is still loading */\r\n isLoading: boolean;\r\n /** Whether multi-tenant mode is enabled (always true) */\r\n isMultiTenant: boolean;\r\n /** Whether hybrid mode is active (multitenant B2C only, no B2B) */\r\n isHybridMode: boolean;\r\n /** Whether B2B tenant creation is enabled */\r\n enableB2B: boolean;\r\n /** Whether B2C tenant creation is enabled */\r\n enableB2C: boolean;\r\n /** Whether URL routing with /t/{slug} prefix is active */\r\n useUrlRouting: boolean;\r\n}\r\n\r\nconst FeatureConfigContext = createContext<FeatureConfigContextType | undefined>(undefined);\r\n\r\ninterface FeatureConfigProviderProps {\r\n readonly children: ReactNode;\r\n}\r\n\r\n/**\r\n * Provider that loads and exposes application feature configuration.\r\n * This context should be placed early in the component tree, before TenantProvider.\r\n */\r\nexport function FeatureConfigProvider({ children }: FeatureConfigProviderProps): ReactElement {\r\n const [config, setConfig] = useState<FeatureConfig | null>(null);\r\n const [isLoading, setIsLoading] = useState(true);\r\n\r\n useEffect(() => {\r\n const controller = new AbortController();\r\n\r\n configApi.getFeatureConfig(controller.signal)\r\n .then(data => {\r\n if (!controller.signal.aborted) {\r\n setConfig(data);\r\n }\r\n })\r\n .catch(err => {\r\n // Ignore abort errors (expected in StrictMode)\r\n if (err.name === 'CanceledError' || controller.signal.aborted) return;\r\n\r\n console.error('Failed to load feature config:', err);\r\n // Default to multi-tenant if config fails (safer default)\r\n setConfig({\r\n multiTenantEnabled: true,\r\n enableB2B: true,\r\n enableB2C: true,\r\n useUrlRouting: true\r\n });\r\n })\r\n .finally(() => {\r\n if (!controller.signal.aborted) {\r\n setIsLoading(false);\r\n }\r\n });\r\n\r\n return () => controller.abort();\r\n }, []);\r\n\r\n const value: FeatureConfigContextType = {\r\n config,\r\n isLoading,\r\n isMultiTenant: true,\r\n isHybridMode: config?.multiTenantEnabled === true && config?.enableB2B === false && config?.enableB2C === true,\r\n enableB2B: config?.enableB2B ?? true,\r\n enableB2C: config?.enableB2C ?? true,\r\n useUrlRouting: config?.useUrlRouting ?? true,\r\n };\r\n\r\n return (\r\n <FeatureConfigContext.Provider value={value}>\r\n {children}\r\n </FeatureConfigContext.Provider>\r\n );\r\n}\r\n\r\n/**\r\n * Hook to access feature configuration.\r\n * Must be used within a FeatureConfigProvider.\r\n */\r\nexport function useFeatureConfig(): FeatureConfigContextType {\r\n const context = useContext(FeatureConfigContext);\r\n if (!context) {\r\n throw new Error('useFeatureConfig must be used within FeatureConfigProvider');\r\n }\r\n return context;\r\n}\r\n","/**\r\n * Shared permission utilities for wildcard matching and permission checks.\r\n * Used by both AuthContext and usePermissionMatrix hook.\r\n */\r\n\r\nexport const WILDCARD_PERMISSION = '*';\r\n\r\n/**\r\n * Checks if a wildcard permission path covers a target permission path.\r\n *\r\n * Examples:\r\n * - matchesWildcard('*', 'administration.users.read') => true\r\n * - matchesWildcard('administration.*', 'administration.users.read') => true\r\n * - matchesWildcard('administration.users.*', 'administration.users.read') => true\r\n * - matchesWildcard('administration.users', 'administration.users.read') => false\r\n *\r\n * @param wildcardPath - The permission path that may contain wildcards (e.g., \"administration.*\" or \"*\")\r\n * @param targetPath - The specific permission path to check (e.g., \"administration.users.read\")\r\n * @returns true if wildcardPath covers targetPath\r\n */\r\nexport function matchesWildcard(wildcardPath: string, targetPath: string): boolean {\r\n const wildcardLower = wildcardPath.toLowerCase();\r\n const targetLower = targetPath.toLowerCase();\r\n\r\n // Global wildcard matches everything\r\n if (wildcardLower === '*') return true;\r\n\r\n // Exact match\r\n if (wildcardLower === targetLower) return true;\r\n\r\n // Wildcard suffix match (e.g., \"platform.*\" covers \"platform.administration.users.read\")\r\n if (wildcardLower.endsWith('.*')) {\r\n const prefix = wildcardLower.slice(0, -2);\r\n return targetLower.startsWith(prefix + '.') || targetLower === prefix;\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if user has a specific permission based on their permission list.\r\n * Handles both exact matches and wildcard permissions.\r\n *\r\n * @param userPermissions - Array of user's permission paths\r\n * @param targetPermission - The permission to check\r\n * @returns true if user has the permission (directly or via wildcard)\r\n */\r\nexport function hasPermission(userPermissions: string[], targetPermission: string): boolean {\r\n // Check for exact match first\r\n if (userPermissions.includes(targetPermission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard matches\r\n for (const perm of userPermissions) {\r\n if (matchesWildcard(perm, targetPermission)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if user has full access (Super Admin).\r\n * A user has full access if they have the \"*\" permission.\r\n *\r\n * @param permissions - User's permission list\r\n * @returns true if user has full access\r\n */\r\nexport function hasFullAccess(permissions: string[]): boolean {\r\n return permissions.includes(WILDCARD_PERMISSION);\r\n}\r\n\r\n/**\r\n * Checks if a user has access to a navigation path (route-level permission guard).\r\n * Mirrors the backend PermissionSet.HasAccessToPath() logic from NavigationService.\r\n *\r\n * A path like \"administration.users\" is accessible if:\r\n * - User is Super Admin (\"*\" permission)\r\n * - Exact match on the path\r\n * - Any user permission starts with path + \".\" (child permission implies parent access)\r\n * e.g., having \"administration.users.read\" grants access to \"administration\"\r\n * - A wildcard permission covers the path\r\n * e.g., \"administration.*\" covers \"administration.users\"\r\n *\r\n * @param permissions - User's permission paths\r\n * @param permissionPrefix - The navigation path to check (e.g., \"administration.users\")\r\n * @returns true if user has access to this navigation path\r\n */\r\nexport function hasRouteAccess(permissions: string[], permissionPrefix: string): boolean {\r\n if (!permissionPrefix) return true;\r\n\r\n if (hasFullAccess(permissions)) return true;\r\n\r\n const prefix = permissionPrefix.toLowerCase();\r\n const prefixDot = prefix + '.';\r\n\r\n for (const perm of permissions) {\r\n const p = perm.toLowerCase();\r\n\r\n // Bare wildcard fallback (defense-in-depth if hasFullAccess above missed it)\r\n if (p === '*') return true;\r\n\r\n // Exact match\r\n if (p === prefix) return true;\r\n\r\n // Child permission implies parent access\r\n // e.g., \"administration.users.read\" grants access to \"administration\"\r\n if (p.startsWith(prefixDot)) return true;\r\n\r\n // Wildcard match\r\n // e.g., \"administration.users.*\" → prefix \"administration.users.\"\r\n if (p.endsWith('.*')) {\r\n const wildcardBase = p.slice(0, -1); // keep trailing dot: \"administration.users.\"\r\n if (prefix.startsWith(wildcardBase) || prefixDot.startsWith(wildcardBase)) return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n","import { createContext, useContext, useEffect, useState, useCallback, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { api } from '@/services/api/apiClient';\r\nimport { matchesWildcard, hasFullAccess as checkFullAccess } from '@/utils/permissions';\r\nimport type { UserTenantDto } from '@/services/api/adminApi';\r\n\r\nexport interface User {\r\n id: string;\r\n email: string;\r\n firstName: string | null;\r\n lastName: string | null;\r\n roles: string[];\r\n permissions: string[];\r\n tenants: UserTenantDto[];\r\n}\r\n\r\ninterface AuthContextType {\r\n user: User | null;\r\n isAuthenticated: boolean;\r\n isLoading: boolean;\r\n login: (token: string, user: User) => void;\r\n logout: () => Promise<void>;\r\n hasPermission: (permission: string) => boolean;\r\n refreshPermissions: () => Promise<void>;\r\n}\r\n\r\nconst AuthContext = createContext<AuthContextType | undefined>(undefined);\r\n\r\nconst TOKEN_KEY = 'token';\r\nconst USER_KEY = 'user';\r\n\r\nexport function AuthProvider({ children }: { children: ReactNode }): ReactElement | null {\r\n const [user, setUser] = useState<User | null>(() => {\r\n if (typeof window !== 'undefined') {\r\n const saved = localStorage.getItem(USER_KEY);\r\n if (saved) {\r\n try {\r\n return JSON.parse(saved);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n }\r\n return null;\r\n });\r\n\r\n const [isLoading, setIsLoading] = useState(true);\r\n\r\n const login = useCallback((token: string, userData: User) => {\r\n localStorage.setItem(TOKEN_KEY, token);\r\n localStorage.setItem(USER_KEY, JSON.stringify(userData));\r\n setUser(userData);\r\n }, []);\r\n\r\n const logout = useCallback(async () => {\r\n try {\r\n const token = localStorage.getItem(TOKEN_KEY);\r\n if (token) {\r\n await api.post('/api/auth/logout');\r\n }\r\n } catch {\r\n // Ignore errors - we still want to clear local state\r\n }\r\n localStorage.removeItem(TOKEN_KEY);\r\n localStorage.removeItem('refreshToken');\r\n localStorage.removeItem(USER_KEY);\r\n setUser(null);\r\n window.location.href = '/login';\r\n }, []);\r\n\r\n const hasPermission = useCallback((permission: string): boolean => {\r\n if (!user) return false;\r\n\r\n // Super Admin has full access (using shared utility)\r\n if (checkFullAccess(user.permissions)) {\r\n return true;\r\n }\r\n\r\n // Check for exact match\r\n if (user.permissions.includes(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard matches using shared utility\r\n for (const userPerm of user.permissions) {\r\n if (matchesWildcard(userPerm, permission)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n }, [user]);\r\n\r\n // Refresh user permissions from server (call after role/permission modifications)\r\n const refreshPermissions = useCallback(async () => {\r\n const token = localStorage.getItem(TOKEN_KEY);\r\n if (!token) return;\r\n\r\n try {\r\n const userData = await api.get<User>('/api/auth/me');\r\n setUser(userData);\r\n localStorage.setItem(USER_KEY, JSON.stringify(userData));\r\n } catch (err) {\r\n console.error('Failed to refresh permissions:', err);\r\n }\r\n }, []);\r\n\r\n useEffect(() => {\r\n const token = localStorage.getItem(TOKEN_KEY);\r\n if (token && !user) {\r\n api.get<User>('/api/auth/me')\r\n .then(userData => {\r\n setUser(userData);\r\n localStorage.setItem(USER_KEY, JSON.stringify(userData));\r\n })\r\n .catch(() => {\r\n localStorage.removeItem(TOKEN_KEY);\r\n localStorage.removeItem('refreshToken');\r\n localStorage.removeItem(USER_KEY);\r\n })\r\n .finally(() => setIsLoading(false));\r\n } else {\r\n setIsLoading(false);\r\n }\r\n }, [user]);\r\n\r\n return (\r\n <AuthContext.Provider value={{\r\n user,\r\n isAuthenticated: !!user,\r\n isLoading,\r\n login,\r\n logout,\r\n hasPermission,\r\n refreshPermissions,\r\n }}>\r\n {children}\r\n </AuthContext.Provider>\r\n );\r\n}\r\n\r\nexport function useAuth(): AuthContextType {\r\n const context = useContext(AuthContext);\r\n if (context === undefined) {\r\n throw new Error('useAuth must be used within an AuthProvider');\r\n }\r\n return context;\r\n}\r\n\r\n/**\r\n * Safe version of useAuth that returns null instead of throwing\r\n * when used outside AuthProvider (e.g., during error boundary recovery).\r\n */\r\nexport function useAuthSafe(): AuthContextType | null {\r\n return useContext(AuthContext) ?? null;\r\n}\r\n","/**\r\n * Checks if a license has all features enabled (wildcard license).\r\n */\r\nexport function hasAllLicenseFeatures(enabledFeatures: string[]): boolean {\r\n return enabledFeatures.includes('*');\r\n}\r\n","import { createContext, useContext, useEffect, useState, useCallback, useRef, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { api } from '@/services/api/apiClient';\r\nimport { hasAllLicenseFeatures } from '@/utils/licenseUtils';\r\n\r\nexport interface LicenseInfo {\r\n productId: string;\r\n companyId: string;\r\n companyName: string | null;\r\n edition: string;\r\n status: string;\r\n activatedAt: string | null;\r\n expiresAt: string;\r\n daysUntilExpiration: number;\r\n isValid: boolean;\r\n isInTrial: boolean;\r\n trialDaysRemaining: number;\r\n isReadOnlyMode: boolean;\r\n enabledFeatures: string[];\r\n limits: LicenseLimits;\r\n maxUsers: number;\r\n isPerpetual: boolean;\r\n gracePeriodDays: number;\r\n renewalDate: string | null;\r\n isInGracePeriod: boolean;\r\n hasApiKey: boolean;\r\n machineFingerprint: string | null;\r\n instanceId: string | null;\r\n machineName: string | null;\r\n licenseServerId: string | null;\r\n availableFeatures?: LicenseFeatureGroup[];\r\n}\r\n\r\nexport interface LicenseFeatureGroup {\r\n group: string;\r\n features: string[];\r\n}\r\n\r\nexport interface LicenseLimits {\r\n users: number;\r\n tenants: number;\r\n custom: Record<string, number>;\r\n}\r\n\r\nexport interface LicenseActivationResult {\r\n success: boolean;\r\n errorMessage?: string;\r\n license?: LicenseInfo;\r\n}\r\n\r\nexport interface LicenseUsage {\r\n activeUsers: number;\r\n activeTenants: number;\r\n}\r\n\r\ninterface LicenseContextType {\r\n license: LicenseInfo | null;\r\n usage: LicenseUsage | null;\r\n isLoading: boolean;\r\n error: string | null;\r\n isFeatureEnabled: (feature: string) => boolean;\r\n isReadOnlyMode: boolean;\r\n refreshLicense: () => Promise<void>;\r\n activateLicense: (key: string, apiKey?: string) => Promise<{ success: boolean; error?: string }>;\r\n}\r\n\r\nconst LicenseContext = createContext<LicenseContextType | undefined>(undefined);\r\n\r\nexport function LicenseProvider({ children }: { children: ReactNode }): ReactElement {\r\n const [license, setLicense] = useState<LicenseInfo | null>(null);\r\n const [usage, setUsage] = useState<LicenseUsage | null>(null);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n const abortControllerRef = useRef<AbortController | null>(null);\r\n\r\n const fetchLicense = useCallback(async (signal?: AbortSignal) => {\r\n try {\r\n setIsLoading(true);\r\n setError(null);\r\n const [licenseData, usageData] = await Promise.all([\r\n api.get<LicenseInfo>('/api/administration/license', { signal }),\r\n api.get<LicenseUsage>('/api/administration/license/usage', { signal }).catch(() => null),\r\n ]);\r\n if (!signal?.aborted) {\r\n setLicense(licenseData);\r\n setUsage(usageData);\r\n }\r\n } catch (err) {\r\n // Ignore abort errors (expected in StrictMode)\r\n if (err instanceof Error && err.name === 'CanceledError') return;\r\n if (signal?.aborted) return;\r\n\r\n setError(err instanceof Error ? err.message : 'Failed to load license info');\r\n console.error('Failed to fetch license:', err);\r\n } finally {\r\n if (!signal?.aborted) {\r\n setIsLoading(false);\r\n }\r\n }\r\n }, []);\r\n\r\n useEffect(() => {\r\n abortControllerRef.current?.abort();\r\n const controller = new AbortController();\r\n abortControllerRef.current = controller;\r\n\r\n fetchLicense(controller.signal);\r\n\r\n return () => controller.abort();\r\n }, [fetchLicense]);\r\n\r\n const isFeatureEnabled = useCallback((feature: string): boolean => {\r\n if (!license) return false;\r\n if (hasAllLicenseFeatures(license.enabledFeatures)) return true;\r\n return license.enabledFeatures.some(f => f.toLowerCase() === feature.toLowerCase());\r\n }, [license]);\r\n\r\n const refreshLicense = useCallback(async () => {\r\n abortControllerRef.current?.abort();\r\n const controller = new AbortController();\r\n abortControllerRef.current = controller;\r\n await fetchLicense(controller.signal);\r\n }, [fetchLicense]);\r\n\r\n const activateLicense = useCallback(async (key: string, apiKey?: string) => {\r\n try {\r\n const result = await api.post<LicenseActivationResult>(\r\n '/api/administration/license/activate',\r\n { licenseKey: key, apiKey: apiKey || null }\r\n );\r\n if (result.success) {\r\n await refreshLicense();\r\n return { success: true };\r\n }\r\n return { success: false, error: result.errorMessage || 'Activation failed' };\r\n } catch (err) {\r\n const errorMessage = err instanceof Error ? err.message : 'Activation failed';\r\n return { success: false, error: errorMessage };\r\n }\r\n }, [refreshLicense]);\r\n\r\n return (\r\n <LicenseContext.Provider value={{\r\n license,\r\n usage,\r\n isLoading,\r\n error,\r\n isFeatureEnabled,\r\n isReadOnlyMode: license?.isReadOnlyMode ?? false,\r\n refreshLicense,\r\n activateLicense,\r\n }}>\r\n {children}\r\n </LicenseContext.Provider>\r\n );\r\n}\r\n\r\nexport function useLicense(): LicenseContextType {\r\n const context = useContext(LicenseContext);\r\n if (context === undefined) {\r\n throw new Error('useLicense must be used within a LicenseProvider');\r\n }\r\n return context;\r\n}\r\n","import { createContext, useContext, useEffect, useState, useCallback, useRef, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { adminApi, type UserTenantDto } from '@/services/api/adminApi';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { useFeatureConfig } from '@/contexts/FeatureConfigContext';\r\nimport { hasFullAccess } from '@/utils/permissions';\r\n\r\ninterface TenantContextType {\r\n /** Current active tenant */\r\n currentTenant: UserTenantDto | null;\r\n /** All tenants the user has access to */\r\n userTenants: UserTenantDto[];\r\n /** B2B tenants (Business type) */\r\n b2bTenants: UserTenantDto[];\r\n /** B2C tenant (Personal type) - user's personal workspace */\r\n personalTenant: UserTenantDto | null;\r\n /** Loading state for tenant operations */\r\n isLoading: boolean;\r\n /** Error state */\r\n error: string | null;\r\n /** Switch to a different tenant */\r\n switchTenant: (tenantId: string) => Promise<void>;\r\n /** Set tenant as user's default */\r\n setDefaultTenant: (tenantId: string) => Promise<void>;\r\n /** Clear default tenant (Global mode - no specific default) */\r\n clearDefaultTenant: () => Promise<void>;\r\n /** Refresh tenant list from server */\r\n refreshTenants: () => Promise<void>;\r\n /** Check if user has multiple tenants */\r\n hasMultipleTenants: boolean;\r\n /** Check if user is owner of current tenant */\r\n isCurrentTenantOwner: boolean;\r\n /** Check if user has global administrative roles (Super Admin) */\r\n hasGlobalRoles: boolean;\r\n /** Whether user is viewing all tenants (global view) */\r\n isGlobalView: boolean;\r\n /** Enter global view mode (see all tenants data) */\r\n enterGlobalView: () => Promise<void>;\r\n /** Exit global view mode (return to specific tenant) */\r\n exitGlobalView: () => Promise<void>;\r\n}\r\n\r\nconst TenantContext = createContext<TenantContextType | undefined>(undefined);\r\n\r\nconst CURRENT_TENANT_KEY = 'currentTenantId';\r\nconst CURRENT_TENANT_SLUG_KEY = 'currentTenantSlug';\r\nconst GLOBAL_VIEW_KEY = 'isGlobalView';\r\n\r\nexport function TenantProvider({ children }: { children: ReactNode }): ReactElement {\r\n const { isAuthenticated, user, refreshPermissions } = useAuth();\r\n const { isLoading: configLoading } = useFeatureConfig();\r\n const [currentTenant, setCurrentTenant] = useState<UserTenantDto | null>(null);\r\n const [userTenants, setUserTenants] = useState<UserTenantDto[]>([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n const [isGlobalView, setIsGlobalView] = useState<boolean>(() => {\r\n // Restore global view state from localStorage\r\n return localStorage.getItem(GLOBAL_VIEW_KEY) === 'true';\r\n });\r\n\r\n // Track if we've initialized tenants to avoid duplicate processing\r\n const hasInitialized = useRef(false);\r\n // Track current tenants key to avoid unnecessary re-initialization\r\n const currentTenantsKeyRef = useRef<string>('');\r\n\r\n // Categorize tenants by type\r\n const b2bTenants = userTenants.filter(t => t.type === 'Business');\r\n const personalTenant = userTenants.find(t => t.type === 'Personal') || null;\r\n\r\n // Initialize tenants from user data (no API call - data comes from AuthContext)\r\n const initializeTenants = useCallback((tenants: UserTenantDto[], hasGlobalRoles: boolean) => {\r\n setUserTenants(tenants);\r\n\r\n if (tenants.length === 0) {\r\n setCurrentTenant(null);\r\n setIsGlobalView(false);\r\n localStorage.removeItem(CURRENT_TENANT_KEY);\r\n localStorage.removeItem(CURRENT_TENANT_SLUG_KEY);\r\n localStorage.removeItem(GLOBAL_VIEW_KEY);\r\n setIsLoading(false);\r\n return;\r\n }\r\n\r\n // Priority 1: Restore last-used tenant from localStorage (user's most recent choice)\r\n // This preserves manual tenant switches across page reloads\r\n const savedTenantId = localStorage.getItem(CURRENT_TENANT_KEY);\r\n const savedTenant = savedTenantId\r\n ? tenants.find(t => t.id === savedTenantId)\r\n : null;\r\n\r\n if (savedTenant?.status === 'Active') {\r\n setCurrentTenant(savedTenant);\r\n setIsGlobalView(false);\r\n localStorage.setItem(CURRENT_TENANT_KEY, savedTenant.id);\r\n localStorage.setItem(CURRENT_TENANT_SLUG_KEY, savedTenant.slug);\r\n localStorage.removeItem(GLOBAL_VIEW_KEY);\r\n setIsLoading(false);\r\n return;\r\n }\r\n\r\n // Priority 2: Check if user explicitly chose Global view (stored in localStorage)\r\n const wasInGlobalView = localStorage.getItem(GLOBAL_VIEW_KEY) === 'true';\r\n if (wasInGlobalView) {\r\n setIsGlobalView(true);\r\n setCurrentTenant(null);\r\n setIsLoading(false);\r\n return;\r\n }\r\n\r\n // Priority 3: API isDefault (user's configured default for fresh sessions)\r\n // Only used when no localStorage selection exists (new device, cleared storage)\r\n const apiDefaultTenant = tenants.find(t => t.isDefault && t.status === 'Active');\r\n if (apiDefaultTenant) {\r\n setCurrentTenant(apiDefaultTenant);\r\n setIsGlobalView(false);\r\n localStorage.setItem(CURRENT_TENANT_KEY, apiDefaultTenant.id);\r\n localStorage.setItem(CURRENT_TENANT_SLUG_KEY, apiDefaultTenant.slug);\r\n localStorage.removeItem(GLOBAL_VIEW_KEY);\r\n setIsLoading(false);\r\n return;\r\n }\r\n\r\n // Priority 3.5: Admin with no default → enter Global view\r\n // When admin explicitly chose \"Global\" (cleared all defaults), restore that preference\r\n if (hasGlobalRoles) {\r\n setIsGlobalView(true);\r\n setCurrentTenant(null);\r\n localStorage.setItem(GLOBAL_VIEW_KEY, 'true');\r\n setIsLoading(false);\r\n return;\r\n }\r\n\r\n // Priority 4: Fall back to first active tenant\r\n const firstActive = tenants.find(t => t.status === 'Active');\r\n setCurrentTenant(firstActive || null);\r\n if (firstActive) {\r\n localStorage.setItem(CURRENT_TENANT_KEY, firstActive.id);\r\n localStorage.setItem(CURRENT_TENANT_SLUG_KEY, firstActive.slug);\r\n }\r\n\r\n setIsLoading(false);\r\n }, []);\r\n\r\n // Switch to a different tenant\r\n const switchTenant = useCallback(async (tenantId: string) => {\r\n const tenant = userTenants.find(t => t.id === tenantId);\r\n if (!tenant) {\r\n throw new Error('Tenant not found');\r\n }\r\n\r\n if (tenant.status !== 'Active') {\r\n throw new Error('Cannot switch to inactive tenant');\r\n }\r\n\r\n // Exit global view when switching to a specific tenant\r\n setIsGlobalView(false);\r\n localStorage.removeItem(GLOBAL_VIEW_KEY);\r\n\r\n setCurrentTenant(tenant);\r\n localStorage.setItem(CURRENT_TENANT_KEY, tenantId);\r\n localStorage.setItem(CURRENT_TENANT_SLUG_KEY, tenant.slug);\r\n\r\n // Refresh user permissions for the new tenant context\r\n // This ensures RouteGuard and hasPermission() use the correct permissions\r\n await refreshPermissions();\r\n }, [userTenants, refreshPermissions]);\r\n\r\n // Enter global view mode (see data from all tenants)\r\n const enterGlobalView = useCallback(async () => {\r\n setIsGlobalView(true);\r\n localStorage.setItem(GLOBAL_VIEW_KEY, 'true');\r\n // Clear current tenant to indicate global view\r\n setCurrentTenant(null);\r\n localStorage.removeItem(CURRENT_TENANT_KEY);\r\n localStorage.removeItem(CURRENT_TENANT_SLUG_KEY);\r\n await refreshPermissions();\r\n }, [refreshPermissions]);\r\n\r\n // Exit global view mode (return to a specific tenant)\r\n const exitGlobalView = useCallback(async () => {\r\n setIsGlobalView(false);\r\n localStorage.removeItem(GLOBAL_VIEW_KEY);\r\n // Restore default or first active tenant\r\n const defaultTenant = userTenants.find(t => t.isDefault && t.status === 'Active');\r\n const firstActive = userTenants.find(t => t.status === 'Active');\r\n const selectedTenant = defaultTenant || firstActive || null;\r\n if (selectedTenant) {\r\n setCurrentTenant(selectedTenant);\r\n localStorage.setItem(CURRENT_TENANT_KEY, selectedTenant.id);\r\n localStorage.setItem(CURRENT_TENANT_SLUG_KEY, selectedTenant.slug);\r\n }\r\n await refreshPermissions();\r\n }, [userTenants, refreshPermissions]);\r\n\r\n // Set tenant as user's default\r\n const setDefaultTenant = useCallback(async (tenantId: string) => {\r\n try {\r\n await adminApi.myTenants.setDefault(tenantId);\r\n\r\n // Update local state\r\n setUserTenants(prev => {\r\n const updated = prev.map(t => ({\r\n ...t,\r\n isDefault: t.id === tenantId\r\n }));\r\n return updated;\r\n });\r\n\r\n // Also switch to this tenant\r\n await switchTenant(tenantId);\r\n } catch (err) {\r\n console.error('[TenantContext] Failed to set default tenant:', err);\r\n throw err;\r\n }\r\n }, [switchTenant]);\r\n\r\n // Clear default tenant (Global mode)\r\n const clearDefaultTenant = useCallback(async () => {\r\n try {\r\n await adminApi.myTenants.clearDefault();\r\n\r\n // Update local state - no tenant is default\r\n setUserTenants(prev => prev.map(t => ({ ...t, isDefault: false })));\r\n\r\n // Enter global view since user explicitly chose \"no default\"\r\n await enterGlobalView();\r\n } catch (err) {\r\n console.error('[TenantContext] Failed to clear default tenant:', err);\r\n throw err;\r\n }\r\n }, [enterGlobalView]);\r\n\r\n // Refresh tenants from server (triggers AuthContext refresh which updates user.tenants)\r\n const refreshTenants = useCallback(async () => {\r\n setIsLoading(true);\r\n setError(null);\r\n try {\r\n // Refresh user data from AuthContext which includes tenants\r\n await refreshPermissions();\r\n } catch (err) {\r\n console.error('Failed to refresh tenants:', err);\r\n setError('Failed to refresh tenants');\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }, [refreshPermissions]);\r\n\r\n // Sync tenants from user data (user.tenants comes from /api/auth/me)\r\n // This eliminates the duplicate API call to /api/tenants/my\r\n useEffect(() => {\r\n if (!isAuthenticated || !user) {\r\n // Only update if we actually have tenants to clear\r\n if (hasInitialized.current) {\r\n setUserTenants([]);\r\n setCurrentTenant(null);\r\n setIsLoading(false);\r\n hasInitialized.current = false;\r\n currentTenantsKeyRef.current = '';\r\n }\r\n return;\r\n }\r\n\r\n // Use tenants from user object (loaded by AuthContext from /api/auth/me)\r\n const tenants = user.tenants || [];\r\n\r\n // Only initialize if user changed or on first load (using ref to avoid dependency on userTenants)\r\n // Include status and isDefault in key to detect changes\r\n const tenantsKey = tenants.map(t => `${t.id}:${t.status}:${t.isDefault}`).sort((a, b) => a.localeCompare(b)).join(',');\r\n\r\n if (!hasInitialized.current || tenantsKey !== currentTenantsKeyRef.current) {\r\n hasInitialized.current = true;\r\n currentTenantsKeyRef.current = tenantsKey;\r\n const hasGlobal = hasFullAccess(user.permissions || []);\r\n initializeTenants(tenants, hasGlobal);\r\n\r\n // After tenant selection, refresh to scope permissions to the selected tenant\r\n // Only when a specific tenant was selected (slug in localStorage), not in Global View\r\n const selectedSlug = localStorage.getItem(CURRENT_TENANT_SLUG_KEY);\r\n if (selectedSlug) {\r\n refreshPermissions().catch(err =>\r\n console.error('[TenantContext] Failed to refresh permissions after tenant init:', err));\r\n }\r\n }\r\n }, [isAuthenticated, user, initializeTenants, refreshPermissions]);\r\n\r\n // Clear tenant when user logs out\r\n useEffect(() => {\r\n if (!isAuthenticated) {\r\n setCurrentTenant(null);\r\n setUserTenants([]);\r\n setIsGlobalView(false);\r\n localStorage.removeItem(CURRENT_TENANT_KEY);\r\n localStorage.removeItem(CURRENT_TENANT_SLUG_KEY);\r\n localStorage.removeItem(GLOBAL_VIEW_KEY);\r\n }\r\n }, [isAuthenticated]);\r\n\r\n // Check if user has global administrative roles (Super Admin)\r\n const userHasGlobalRoles = user ? hasFullAccess(user.permissions || []) : false;\r\n\r\n const value: TenantContextType = {\r\n currentTenant,\r\n userTenants,\r\n b2bTenants,\r\n personalTenant,\r\n isLoading: isLoading || configLoading,\r\n error,\r\n switchTenant,\r\n setDefaultTenant,\r\n clearDefaultTenant,\r\n refreshTenants,\r\n hasMultipleTenants: userTenants.filter(t => t.status === 'Active').length > 1,\r\n isCurrentTenantOwner: currentTenant?.isOwner ?? false,\r\n hasGlobalRoles: userHasGlobalRoles,\r\n isGlobalView,\r\n enterGlobalView,\r\n exitGlobalView\r\n };\r\n\r\n return (\r\n <TenantContext.Provider value={value}>\r\n {children}\r\n </TenantContext.Provider>\r\n );\r\n}\r\n\r\nexport function useTenant(): TenantContextType {\r\n const context = useContext(TenantContext);\r\n if (context === undefined) {\r\n throw new Error('useTenant must be used within a TenantProvider');\r\n }\r\n return context;\r\n}\r\n","import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { api } from '@/services/api/apiClient';\r\nimport { useAuth } from './AuthContext';\r\nimport { useTenant } from './TenantContext';\r\n\r\nexport interface FavoriteModule {\r\n id: string;\r\n code: string;\r\n label: string;\r\n icon: string;\r\n route: string;\r\n applicationId?: string;\r\n applicationCode: string;\r\n applicationLabel: string;\r\n displayOrder?: number;\r\n}\r\n\r\ninterface FavoriteModuleDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n icon: string | null;\r\n route: string | null;\r\n applicationId: string;\r\n applicationCode: string;\r\n applicationLabel: string;\r\n displayOrder: number;\r\n}\r\n\r\nconst MAX_FAVORITES = 5;\r\n\r\ninterface FavoritesContextType {\r\n favorites: FavoriteModule[];\r\n loading: boolean;\r\n error: string | null;\r\n addFavorite: (module: FavoriteModule) => Promise<boolean>;\r\n removeFavorite: (moduleId: string) => Promise<boolean>;\r\n toggleFavorite: (module: FavoriteModule) => Promise<boolean>;\r\n isFavorite: (moduleId: string) => boolean;\r\n reorderFavorites: (newOrder: FavoriteModule[]) => Promise<boolean>;\r\n canAddMore: boolean;\r\n maxFavorites: number;\r\n reload: () => Promise<void>;\r\n // Expand sidebar favorites section when navigating from quick access\r\n expandFavorites: boolean;\r\n triggerExpand: () => void;\r\n clearExpand: () => void;\r\n // Whether favorites operations require a tenant context\r\n requiresTenant: boolean;\r\n}\r\n\r\nconst FavoritesContext = createContext<FavoritesContextType | null>(null);\r\n\r\nexport function FavoritesProvider({ children }: { children: ReactNode }): ReactElement {\r\n const { i18n } = useTranslation();\r\n const { isAuthenticated } = useAuth();\r\n const { currentTenant, isLoading: tenantLoading } = useTenant();\r\n const [favorites, setFavorites] = useState<FavoriteModule[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const [expandFavorites, setExpandFavorites] = useState(false);\r\n\r\n const triggerExpand = useCallback(() => {\r\n setExpandFavorites(true);\r\n }, []);\r\n\r\n const clearExpand = useCallback(() => {\r\n setExpandFavorites(false);\r\n }, []);\r\n\r\n const loadFavorites = useCallback(async () => {\r\n try {\r\n setLoading(true);\r\n setError(null);\r\n const data = await api.get<FavoriteModuleDto[]>('/api/navigation/module-favorites');\r\n const mapped = data.map(dto => ({\r\n id: dto.id,\r\n code: dto.code,\r\n label: dto.label,\r\n icon: dto.icon || 'Star',\r\n route: dto.route || '',\r\n applicationId: dto.applicationId,\r\n applicationCode: dto.applicationCode,\r\n applicationLabel: dto.applicationLabel,\r\n displayOrder: dto.displayOrder,\r\n }));\r\n setFavorites(mapped);\r\n } catch (err) {\r\n console.error('Failed to load favorite modules:', err);\r\n setError('Erreur lors du chargement des favoris');\r\n setFavorites([]);\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n useEffect(() => {\r\n // Load favorites when authenticated (with or without tenant context)\r\n // GET endpoint returns all user's favorites when no tenant, filtered when tenant exists\r\n if (isAuthenticated && !tenantLoading) {\r\n loadFavorites();\r\n } else if (!isAuthenticated) {\r\n setFavorites([]);\r\n setError(null);\r\n }\r\n }, [isAuthenticated, tenantLoading, currentTenant, loadFavorites]);\r\n\r\n // Reload favorites when language changes to get translated labels\r\n useEffect(() => {\r\n const handleLanguageChange = () => {\r\n if (isAuthenticated && !tenantLoading) {\r\n loadFavorites();\r\n }\r\n };\r\n\r\n i18n.on('languageChanged', handleLanguageChange);\r\n return () => {\r\n i18n.off('languageChanged', handleLanguageChange);\r\n };\r\n }, [i18n, isAuthenticated, tenantLoading, loadFavorites]);\r\n\r\n const addFavorite = useCallback(async (module: FavoriteModule) => {\r\n // Favorites can be added with or without tenant context\r\n // - With tenant: creates tenant-specific favorite\r\n // - Without tenant: creates global favorite (shown when tenant has no specific favorites)\r\n if (favorites.length >= MAX_FAVORITES) {\r\n return false;\r\n }\r\n if (favorites.some(f => f.id === module.id)) {\r\n return false;\r\n }\r\n\r\n try {\r\n setError(null);\r\n await api.post(`/api/navigation/module-favorites/${module.id}`);\r\n setFavorites(prev => [...prev, { ...module, displayOrder: prev.length + 1 }]);\r\n return true;\r\n } catch (err) {\r\n console.error('Failed to add favorite module:', err);\r\n await loadFavorites();\r\n return false;\r\n }\r\n }, [favorites, loadFavorites]);\r\n\r\n const removeFavorite = useCallback(async (moduleId: string) => {\r\n try {\r\n setError(null);\r\n await api.delete(`/api/navigation/module-favorites/${moduleId}`);\r\n setFavorites(prev => prev.filter(f => f.id !== moduleId));\r\n return true;\r\n } catch (err) {\r\n console.error('Failed to remove favorite module:', err);\r\n await loadFavorites();\r\n return false;\r\n }\r\n }, [loadFavorites]);\r\n\r\n const isFavorite = useCallback((moduleId: string) => {\r\n return favorites.some(f => f.id === moduleId);\r\n }, [favorites]);\r\n\r\n const toggleFavorite = useCallback(async (module: FavoriteModule) => {\r\n if (isFavorite(module.id)) {\r\n return await removeFavorite(module.id);\r\n } else {\r\n return await addFavorite(module);\r\n }\r\n }, [isFavorite, removeFavorite, addFavorite]);\r\n\r\n const reorderFavorites = useCallback(async (newOrder: FavoriteModule[]) => {\r\n const limitedOrder = newOrder.slice(0, MAX_FAVORITES);\r\n const moduleIds = limitedOrder.map(m => m.id);\r\n\r\n // Optimistic update\r\n const previousFavorites = [...favorites];\r\n setFavorites(limitedOrder.map((m, idx) => ({ ...m, displayOrder: idx + 1 })));\r\n\r\n try {\r\n setError(null);\r\n await api.put('/api/navigation/module-favorites/reorder', moduleIds);\r\n return true;\r\n } catch (err) {\r\n console.error('Failed to reorder favorite modules:', err);\r\n // Rollback on error\r\n setFavorites(previousFavorites);\r\n return false;\r\n }\r\n }, [favorites]);\r\n\r\n const canAddMore = favorites.length < MAX_FAVORITES;\r\n // Favorites no longer require tenant context - they can be global or tenant-specific\r\n const requiresTenant = false;\r\n\r\n return (\r\n <FavoritesContext.Provider\r\n value={{\r\n favorites,\r\n loading,\r\n error,\r\n addFavorite,\r\n removeFavorite,\r\n toggleFavorite,\r\n isFavorite,\r\n reorderFavorites,\r\n canAddMore,\r\n maxFavorites: MAX_FAVORITES,\r\n reload: loadFavorites,\r\n expandFavorites,\r\n triggerExpand,\r\n clearExpand,\r\n requiresTenant,\r\n }}\r\n >\r\n {children}\r\n </FavoritesContext.Provider>\r\n );\r\n}\r\n\r\nexport function useFavorites(): FavoritesContextType {\r\n const context = useContext(FavoritesContext);\r\n if (!context) {\r\n throw new Error('useFavorites must be used within a FavoritesProvider');\r\n }\r\n return context;\r\n}\r\n","import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\n\r\nconst SIDEBAR_STORAGE_KEY = 'smartstack-sidebar-collapsed';\r\n\r\ninterface SidebarContextType {\r\n isCollapsed: boolean;\r\n toggleCollapsed: () => void;\r\n setCollapsed: (collapsed: boolean) => void;\r\n sidebarWidth: number;\r\n}\r\n\r\nconst SidebarContext = createContext<SidebarContextType | undefined>(undefined);\r\n\r\nfunction loadCollapsedState(): boolean {\r\n if (typeof window === 'undefined') return false;\r\n try {\r\n const saved = localStorage.getItem(SIDEBAR_STORAGE_KEY);\r\n return saved === 'true';\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nexport function SidebarProvider({ children }: { children: ReactNode }): ReactElement {\r\n const [isCollapsed, setIsCollapsed] = useState(loadCollapsedState);\r\n\r\n useEffect(() => {\r\n localStorage.setItem(SIDEBAR_STORAGE_KEY, String(isCollapsed));\r\n }, [isCollapsed]);\r\n\r\n const toggleCollapsed = useCallback(() => {\r\n setIsCollapsed(prev => !prev);\r\n }, []);\r\n\r\n const setCollapsed = useCallback((collapsed: boolean) => {\r\n setIsCollapsed(collapsed);\r\n }, []);\r\n\r\n const sidebarWidth = isCollapsed ? 64 : 256;\r\n\r\n return (\r\n <SidebarContext.Provider value={{\r\n isCollapsed,\r\n toggleCollapsed,\r\n setCollapsed,\r\n sidebarWidth,\r\n }}>\r\n {children}\r\n </SidebarContext.Provider>\r\n );\r\n}\r\n\r\nexport function useSidebar(): SidebarContextType {\r\n const context = useContext(SidebarContext);\r\n if (context === undefined) {\r\n throw new Error('useSidebar must be used within a SidebarProvider');\r\n }\r\n return context;\r\n}\r\n","/**\r\n * Route segments that are NOT part of the dynamic navigation hierarchy.\r\n * These are system/auth routes that should be ignored when parsing the\r\n * current application/module from the URL pathname.\r\n *\r\n * Update this list when adding new top-level system route segments.\r\n */\r\nexport const SYSTEM_ROUTE_SEGMENTS = [\r\n 'login',\r\n 'callback',\r\n 'logout',\r\n 'onboarding',\r\n 'system',\r\n 'applications',\r\n 'notifications',\r\n] as const;\r\n\r\nexport type SystemRouteSegment = (typeof SYSTEM_ROUTE_SEGMENTS)[number];\r\n","import { createContext, useContext, useState, useEffect, useCallback, useRef, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useLocation } from 'react-router-dom';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { api } from '@/services/api/apiClient';\r\nimport { useTenant } from './TenantContext';\r\nimport { SYSTEM_ROUTE_SEGMENTS } from '@/constants/routes';\r\n\r\nexport interface ApplicationDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n description: string | null;\r\n icon: string | null;\r\n route: string | null;\r\n displayOrder: number;\r\n zone: string | null;\r\n accessLevel: string | null;\r\n componentKey: string;\r\n permissionPath: string;\r\n isOpen: boolean;\r\n requiredFeature: string | null;\r\n modules: ModuleDto[];\r\n}\r\n\r\nexport interface ModuleDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n icon: string | null;\r\n route: string | null;\r\n displayOrder: number;\r\n componentKey: string;\r\n permissionPath: string;\r\n requiredFeature: string | null;\r\n sections: SectionDto[];\r\n}\r\n\r\nexport interface SectionDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n icon: string | null;\r\n route: string | null;\r\n displayOrder: number;\r\n componentKey: string;\r\n permissionPath: string;\r\n resources: ResourceDto[];\r\n}\r\n\r\nexport interface ResourceDto {\r\n id: string;\r\n code: string;\r\n label: string;\r\n route: string | null;\r\n displayOrder: number;\r\n componentKey: string;\r\n permissionPath: string;\r\n}\r\n\r\nexport interface MenuDto {\r\n applications: ApplicationDto[];\r\n}\r\n\r\nexport interface FavoriteApplication {\r\n id: string;\r\n code: string;\r\n label: string;\r\n icon: string | null;\r\n route: string | null;\r\n displayOrder: number;\r\n}\r\n\r\ninterface NavigationContextType {\r\n menu: MenuDto | null;\r\n favorites: FavoriteApplication[];\r\n currentAppCode: string | null;\r\n currentModuleCode: string | null;\r\n isLoading: boolean;\r\n getCurrentApplication: () => ApplicationDto | undefined;\r\n getCurrentModule: () => ModuleDto | undefined;\r\n getApplicationsByZone: (zone: string) => ApplicationDto[];\r\n /** Alias for getApplicationsByZone — filters applications by their zone (platform, personal, business). */\r\n getApplicationsForContext: (zone: string) => ApplicationDto[];\r\n toggleFavorite: (applicationId: string) => Promise<void>;\r\n isFavorite: (applicationId: string) => boolean;\r\n refreshMenu: () => Promise<void>;\r\n}\r\n\r\nconst NavigationContext = createContext<NavigationContextType | undefined>(undefined);\r\n\r\nfunction parseNavigationPath(pathname: string): { app: string | null; module: string | null } {\r\n let parts = pathname.split('/').filter(Boolean);\r\n\r\n // Handle multi-tenant URL prefix: /t/{tenant-slug}/{application}/{module}\r\n // Skip the tenant prefix if present\r\n if (parts.length >= 2 && parts[0] === 't') {\r\n parts = parts.slice(2); // Remove 't' and tenant-slug\r\n }\r\n\r\n // Route structure: /{application}/{module}\r\n // Examples:\r\n // /administration -> app=administration, module=null\r\n // /administration/users -> app=administration, module=users\r\n // /t/acme/administration/users -> app=administration, module=users\r\n\r\n if (parts.length === 0) {\r\n return { app: null, module: null };\r\n }\r\n\r\n // Skip system routes (login, callback, etc.)\r\n const systemRoutes = SYSTEM_ROUTE_SEGMENTS;\r\n if ((systemRoutes as readonly string[]).includes(parts[0])) {\r\n return { app: null, module: null };\r\n }\r\n\r\n return {\r\n app: parts[0] || null,\r\n module: parts[1] || null,\r\n };\r\n}\r\n\r\nexport function NavigationProvider({ children }: { children: ReactNode }): ReactElement {\r\n const location = useLocation();\r\n const { i18n } = useTranslation();\r\n const { currentTenant, isLoading: tenantLoading, isGlobalView } = useTenant();\r\n const [menu, setMenu] = useState<MenuDto | null>(null);\r\n const [favorites, setFavorites] = useState<FavoriteApplication[]>([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n\r\n // Track if initial menu load has been done\r\n const hasLoadedRef = useRef(false);\r\n // Track previous tenant ID to detect actual changes\r\n const prevTenantIdRef = useRef<string | undefined>(undefined);\r\n // Version counter: ensures only the latest refresh applies its results\r\n // (handles concurrent refreshes from tenant switches and StrictMode double-renders)\r\n const refreshVersionRef = useRef(0);\r\n\r\n // FIX: Synchronous stale-menu detection during render.\r\n // When the tenant context changes, the menu is stale (loaded for the old tenant).\r\n // We must set isLoading=true DURING render (before effects run) so that\r\n // ProtectedRoute shows PageLoader instead of evaluating with stale data.\r\n // React 18 supports this pattern: https://react.dev/reference/react/useState#storing-information-from-previous-renders\r\n const contextKey = isGlobalView ? 'global' : (currentTenant?.id ?? 'none');\r\n if (hasLoadedRef.current && contextKey !== prevTenantIdRef.current && !isLoading) {\r\n setIsLoading(true);\r\n }\r\n\r\n const { app: currentAppCode, module: currentModuleCode } =\r\n parseNavigationPath(location.pathname);\r\n\r\n const fetchMenu = useCallback(async () => {\r\n try {\r\n const data = await api.get<MenuDto>('/api/navigation/menu');\r\n setMenu(data);\r\n } catch (error) {\r\n console.error('Failed to fetch menu:', error);\r\n }\r\n }, []);\r\n\r\n const fetchFavorites = useCallback(async () => {\r\n try {\r\n const data = await api.get<FavoriteApplication[]>('/api/navigation/favorites');\r\n setFavorites(data);\r\n } catch (error) {\r\n console.error('Failed to fetch favorites:', error);\r\n }\r\n }, []);\r\n\r\n const refreshMenu = useCallback(async () => {\r\n const version = ++refreshVersionRef.current;\r\n\r\n setIsLoading(true);\r\n // Menu is filtered by tenant (TenantApplication table) via X-Tenant-Slug header\r\n await fetchMenu();\r\n // Bail if superseded by a newer refresh (e.g. tenant changed during fetch)\r\n if (version !== refreshVersionRef.current) return;\r\n if (currentTenant) {\r\n await fetchFavorites();\r\n if (version !== refreshVersionRef.current) return;\r\n } else {\r\n // Favorites are tenant-scoped — clear when no tenant context\r\n setFavorites([]);\r\n }\r\n setIsLoading(false);\r\n }, [fetchMenu, fetchFavorites, currentTenant]);\r\n\r\n // Unified effect: handles both initial load, tenant changes, and global view transitions.\r\n // Uses a composite key (tenantId + isGlobalView) to detect any context change.\r\n useEffect(() => {\r\n const token = localStorage.getItem('token');\r\n // Composite key: distinguishes \"no tenant\" (undefined) from \"global view\" ('global')\r\n const contextKey = isGlobalView ? 'global' : (currentTenant?.id ?? 'none');\r\n\r\n // Skip if no token or still loading\r\n if (!token || tenantLoading) {\r\n return;\r\n }\r\n\r\n // Case 1: Initial load (never loaded before)\r\n if (!hasLoadedRef.current) {\r\n hasLoadedRef.current = true;\r\n prevTenantIdRef.current = contextKey;\r\n refreshMenu();\r\n return;\r\n }\r\n\r\n // Case 2: Context changed (tenant switch or global view toggle)\r\n if (contextKey !== prevTenantIdRef.current) {\r\n prevTenantIdRef.current = contextKey;\r\n refreshMenu();\r\n }\r\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Intentionally exclude refreshMenu to prevent double-refresh\r\n }, [currentTenant?.id, isGlobalView, tenantLoading]);\r\n\r\n // Reload menu when language changes\r\n useEffect(() => {\r\n const handleLanguageChange = () => {\r\n const token = localStorage.getItem('token');\r\n if (token) {\r\n refreshMenu();\r\n }\r\n };\r\n\r\n i18n.on('languageChanged', handleLanguageChange);\r\n return () => {\r\n i18n.off('languageChanged', handleLanguageChange);\r\n };\r\n }, [i18n, refreshMenu]);\r\n\r\n // Strip core_ or ext_ prefix from code for URL matching\r\n const stripCodePrefix = useCallback((code: string): string => {\r\n if (code.startsWith('core_')) return code.slice(5);\r\n if (code.startsWith('ext_')) return code.slice(4);\r\n return code;\r\n }, []);\r\n\r\n // Extract route segment from a route path (e.g., \"/platform/administration\" -> \"administration\")\r\n const getRouteSegment = useCallback((route: string | null, segmentIndex: number): string | null => {\r\n if (!route) return null;\r\n const parts = route.split('/').filter(Boolean);\r\n return parts[segmentIndex] || null;\r\n }, []);\r\n\r\n const getCurrentApplication = useCallback(() => {\r\n // Match by route segment (e.g., \"/administration\" -> segment[0]=\"administration\")\r\n // or by code with prefix stripped\r\n return menu?.applications.find(a =>\r\n getRouteSegment(a.route, 0) === currentAppCode ||\r\n stripCodePrefix(a.code) === currentAppCode\r\n );\r\n }, [menu, currentAppCode, getRouteSegment, stripCodePrefix]);\r\n\r\n const getCurrentModule = useCallback(() => {\r\n const app = getCurrentApplication();\r\n // Match by route segment (e.g., \"/administration/users\" -> segment[1]=\"users\")\r\n // or by code with prefix stripped\r\n return app?.modules.find(m =>\r\n getRouteSegment(m.route, 1) === currentModuleCode ||\r\n stripCodePrefix(m.code) === currentModuleCode\r\n );\r\n }, [getCurrentApplication, currentModuleCode, getRouteSegment, stripCodePrefix]);\r\n\r\n const getApplicationsByZone = useCallback((zone: string) => {\r\n return menu?.applications.filter(a => a.zone === zone) || [];\r\n }, [menu]);\r\n\r\n const isFavorite = useCallback((applicationId: string) => {\r\n return favorites.some(f => f.id === applicationId);\r\n }, [favorites]);\r\n\r\n const toggleFavorite = useCallback(async (applicationId: string) => {\r\n if (!currentTenant) {\r\n return;\r\n }\r\n try {\r\n if (isFavorite(applicationId)) {\r\n await api.delete(`/api/navigation/favorites/${applicationId}`);\r\n setFavorites(prev => prev.filter(f => f.id !== applicationId));\r\n } else {\r\n await api.post(`/api/navigation/favorites/${applicationId}`, {});\r\n await fetchFavorites();\r\n }\r\n } catch (error) {\r\n console.error('Failed to toggle favorite:', error);\r\n }\r\n }, [currentTenant, isFavorite, fetchFavorites]);\r\n\r\n return (\r\n <NavigationContext.Provider value={{\r\n menu,\r\n favorites,\r\n currentAppCode,\r\n currentModuleCode,\r\n isLoading,\r\n getCurrentApplication,\r\n getCurrentModule,\r\n getApplicationsByZone,\r\n getApplicationsForContext: getApplicationsByZone,\r\n toggleFavorite,\r\n isFavorite,\r\n refreshMenu,\r\n }}>\r\n {children}\r\n </NavigationContext.Provider>\r\n );\r\n}\r\n\r\nexport function useNavigation(): NavigationContextType {\r\n const context = useContext(NavigationContext);\r\n if (context === undefined) {\r\n throw new Error('useNavigation must be used within a NavigationProvider');\r\n }\r\n return context;\r\n}\r\n","import { createContext, useContext, useEffect, useRef, useState, useCallback, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport * as signalR from '@microsoft/signalr';\r\nimport { useAuth } from './AuthContext';\r\nimport { useNavigation } from './NavigationContext';\r\n\r\nexport interface NotificationDto {\r\n id: string;\r\n type: string;\r\n title: string;\r\n message: string;\r\n relatedEntityType: string | null;\r\n relatedEntityId: string | null;\r\n actionUrl: string | null;\r\n isRead: boolean;\r\n readAt: string | null;\r\n createdAt: string;\r\n tenantId: string | null;\r\n tenantName: string | null;\r\n tenantSlug: string | null;\r\n}\r\n\r\ntype NotificationHandler = (notification: NotificationDto) => void;\r\ntype UnreadCountHandler = (count: number) => void;\r\n\r\ninterface SignalRContextValue {\r\n isConnected: boolean;\r\n connectionState: signalR.HubConnectionState;\r\n // Subscribe to notifications (returns unsubscribe function)\r\n onNotification: (handler: NotificationHandler) => () => void;\r\n onUnreadCountUpdate: (handler: UnreadCountHandler) => () => void;\r\n}\r\n\r\nconst SignalRContext = createContext<SignalRContextValue | null>(null);\r\n\r\nexport function SignalRProvider({ children }: { children: ReactNode }): ReactElement {\r\n const { isAuthenticated, refreshPermissions } = useAuth();\r\n const { refreshMenu } = useNavigation();\r\n const connectionRef = useRef<signalR.HubConnection | null>(null);\r\n const [connectionState, setConnectionState] = useState<signalR.HubConnectionState>(\r\n signalR.HubConnectionState.Disconnected\r\n );\r\n\r\n // Event subscribers\r\n const notificationHandlers = useRef<Set<NotificationHandler>>(new Set());\r\n const unreadCountHandlers = useRef<Set<UnreadCountHandler>>(new Set());\r\n const isRefreshingPermissions = useRef(false);\r\n\r\n // Permission sync handler (built-in to avoid extra component)\r\n const handlePermissionsChanged = useCallback(async () => {\r\n if (isRefreshingPermissions.current) return;\r\n isRefreshingPermissions.current = true;\r\n\r\n try {\r\n await Promise.all([refreshPermissions(), refreshMenu()]);\r\n } catch (error) {\r\n console.error('[SignalR] Failed to refresh permissions:', error);\r\n } finally {\r\n isRefreshingPermissions.current = false;\r\n }\r\n }, [refreshPermissions, refreshMenu]);\r\n\r\n // Connect to SignalR hub\r\n const connect = useCallback(async () => {\r\n const token = localStorage.getItem('token');\r\n if (!token) {\r\n return;\r\n }\r\n\r\n // Already connected or connecting\r\n if (connectionRef.current?.state === signalR.HubConnectionState.Connected ||\r\n connectionRef.current?.state === signalR.HubConnectionState.Connecting) {\r\n return;\r\n }\r\n\r\n try {\r\n const connection = new signalR.HubConnectionBuilder()\r\n .withUrl('/hubs/notifications', {\r\n accessTokenFactory: () => token,\r\n })\r\n .withAutomaticReconnect({\r\n nextRetryDelayInMilliseconds: (retryContext) => {\r\n if (retryContext.previousRetryCount === 0) return 0;\r\n if (retryContext.previousRetryCount === 1) return 2000;\r\n if (retryContext.previousRetryCount === 2) return 10000;\r\n if (retryContext.previousRetryCount < 5) return 30000;\r\n return null;\r\n },\r\n })\r\n .configureLogging(signalR.LogLevel.Warning)\r\n .build();\r\n\r\n // Handle incoming notifications\r\n connection.on('ReceiveNotification', (notification: NotificationDto) => {\r\n notificationHandlers.current.forEach(handler => handler(notification));\r\n });\r\n\r\n // Handle unread count updates\r\n connection.on('UnreadCountUpdate', (count: number) => {\r\n unreadCountHandlers.current.forEach(handler => handler(count));\r\n });\r\n\r\n // Handle permissions changes\r\n connection.on('PermissionsChanged', handlePermissionsChanged);\r\n\r\n // Connection state handlers\r\n connection.onreconnecting(() => {\r\n setConnectionState(signalR.HubConnectionState.Reconnecting);\r\n });\r\n\r\n connection.onreconnected(() => {\r\n setConnectionState(signalR.HubConnectionState.Connected);\r\n });\r\n\r\n connection.onclose(() => {\r\n setConnectionState(signalR.HubConnectionState.Disconnected);\r\n });\r\n\r\n await connection.start();\r\n connectionRef.current = connection;\r\n setConnectionState(signalR.HubConnectionState.Connected);\r\n } catch (error) {\r\n console.error('[SignalR] Failed to connect:', error);\r\n setConnectionState(signalR.HubConnectionState.Disconnected);\r\n }\r\n }, [handlePermissionsChanged]);\r\n\r\n // Disconnect from SignalR hub\r\n const disconnect = useCallback(async () => {\r\n if (connectionRef.current) {\r\n try {\r\n await connectionRef.current.stop();\r\n connectionRef.current = null;\r\n setConnectionState(signalR.HubConnectionState.Disconnected);\r\n } catch (error) {\r\n console.error('[SignalR] Failed to disconnect:', error);\r\n }\r\n }\r\n }, []);\r\n\r\n // Connect/disconnect based on auth state\r\n useEffect(() => {\r\n if (isAuthenticated) {\r\n connect();\r\n } else {\r\n disconnect();\r\n }\r\n\r\n return () => {\r\n disconnect();\r\n };\r\n }, [isAuthenticated, connect, disconnect]);\r\n\r\n // Subscribe functions\r\n const onNotification = useCallback((handler: NotificationHandler) => {\r\n notificationHandlers.current.add(handler);\r\n return () => {\r\n notificationHandlers.current.delete(handler);\r\n };\r\n }, []);\r\n\r\n const onUnreadCountUpdate = useCallback((handler: UnreadCountHandler) => {\r\n unreadCountHandlers.current.add(handler);\r\n return () => {\r\n unreadCountHandlers.current.delete(handler);\r\n };\r\n }, []);\r\n\r\n const value: SignalRContextValue = {\r\n isConnected: connectionState === signalR.HubConnectionState.Connected,\r\n connectionState,\r\n onNotification,\r\n onUnreadCountUpdate,\r\n };\r\n\r\n return (\r\n <SignalRContext.Provider value={value}>\r\n {children}\r\n </SignalRContext.Provider>\r\n );\r\n}\r\n\r\nexport function useSignalRContext(): SignalRContextValue {\r\n const context = useContext(SignalRContext);\r\n if (!context) {\r\n throw new Error('useSignalRContext must be used within a SignalRProvider');\r\n }\r\n return context;\r\n}\r\n","import { api } from './apiClient';\r\n\r\n// DTOs\r\nexport interface RegisterRequest {\r\n email: string;\r\n password: string;\r\n firstName: string;\r\n lastName: string;\r\n}\r\n\r\nexport interface RegisterResponse {\r\n message: string;\r\n email: string;\r\n requiresEmailConfirmation: boolean;\r\n}\r\n\r\nexport interface ConfirmEmailResponse {\r\n message: string;\r\n email: string;\r\n}\r\n\r\nexport interface ResendConfirmationResponse {\r\n message: string;\r\n}\r\n\r\nexport interface ForgotPasswordResponse {\r\n message: string;\r\n}\r\n\r\nexport interface ResetPasswordResponse {\r\n message: string;\r\n}\r\n\r\nexport interface LoginResponse {\r\n token: string;\r\n refreshToken: string;\r\n user: {\r\n id: string;\r\n email: string;\r\n firstName: string;\r\n lastName: string;\r\n };\r\n mustChangePassword: boolean;\r\n}\r\n\r\nexport interface ChangePasswordResponse {\r\n message: string;\r\n requireRelogin?: boolean;\r\n}\r\n\r\nexport interface RefreshResponse {\r\n token: string;\r\n refreshToken: string;\r\n user: {\r\n id: string;\r\n email: string;\r\n firstName: string;\r\n lastName: string;\r\n roles: string[];\r\n permissions: string[];\r\n };\r\n}\r\n\r\nexport interface SessionInfoResponse {\r\n sessionId: string;\r\n loginAt: string;\r\n lastActivityAt: string;\r\n timeUntilIdleExpirySeconds: number;\r\n timeUntilAbsoluteExpirySeconds: number;\r\n isWarningThreshold: boolean;\r\n idleTimeoutMinutes: number;\r\n absoluteTimeoutHours: number;\r\n warningThresholdMinutes: number;\r\n}\r\n\r\n// Registration API\r\nexport const authApi = {\r\n /**\r\n * Créer un nouveau compte utilisateur\r\n */\r\n register: async (data: RegisterRequest): Promise<RegisterResponse> => {\r\n return api.post<RegisterResponse>('/api/auth/register', data);\r\n },\r\n\r\n /**\r\n * Confirmer l'adresse email avec le token reçu par email\r\n */\r\n confirmEmail: async (token: string): Promise<ConfirmEmailResponse> => {\r\n return api.post<ConfirmEmailResponse>('/api/auth/confirm-email', { token });\r\n },\r\n\r\n /**\r\n * Renvoyer l'email de confirmation\r\n */\r\n resendConfirmation: async (email: string): Promise<ResendConfirmationResponse> => {\r\n return api.post<ResendConfirmationResponse>('/api/auth/resend-confirmation', { email });\r\n },\r\n\r\n /**\r\n * Demander un lien de réinitialisation de mot de passe\r\n */\r\n forgotPassword: async (email: string): Promise<ForgotPasswordResponse> => {\r\n return api.post<ForgotPasswordResponse>('/api/auth/forgot-password', { email });\r\n },\r\n\r\n /**\r\n * Réinitialiser le mot de passe avec le token reçu par email\r\n */\r\n resetPassword: async (token: string, newPassword: string): Promise<ResetPasswordResponse> => {\r\n return api.post<ResetPasswordResponse>('/api/auth/reset-password', {\r\n token,\r\n newPassword,\r\n });\r\n },\r\n\r\n /**\r\n * Connexion utilisateur\r\n */\r\n login: async (email: string, password: string): Promise<LoginResponse> => {\r\n const response = await api.post<LoginResponse>('/api/auth/login', {\r\n email,\r\n password,\r\n });\r\n\r\n // Store token in localStorage\r\n if (response.token) {\r\n localStorage.setItem('token', response.token);\r\n localStorage.setItem('refreshToken', response.refreshToken);\r\n }\r\n\r\n return response;\r\n },\r\n\r\n /**\r\n * Déconnexion utilisateur\r\n */\r\n logout: async (): Promise<void> => {\r\n try {\r\n await api.post('/api/auth/logout');\r\n } finally {\r\n // Always clear local storage, even if API call fails\r\n localStorage.removeItem('token');\r\n localStorage.removeItem('refreshToken');\r\n localStorage.removeItem('user');\r\n }\r\n },\r\n\r\n /**\r\n * Changer le mot de passe (obligatoire après reset admin)\r\n */\r\n changePassword: async (currentPassword: string, newPassword: string): Promise<ChangePasswordResponse> => {\r\n return api.post<ChangePasswordResponse>('/api/auth/change-password', {\r\n currentPassword,\r\n newPassword,\r\n });\r\n },\r\n\r\n /**\r\n * Rafraîchir le token de session (prolonge la session)\r\n */\r\n refreshSession: async (): Promise<RefreshResponse> => {\r\n const response = await api.post<RefreshResponse>('/api/auth/refresh');\r\n\r\n if (response.token) {\r\n localStorage.setItem('token', response.token);\r\n localStorage.setItem('refreshToken', response.refreshToken);\r\n }\r\n\r\n return response;\r\n },\r\n\r\n /**\r\n * Obtenir les informations de session (temps restant, etc.)\r\n */\r\n getSessionInfo: async (config?: { signal?: AbortSignal }): Promise<SessionInfoResponse> => {\r\n return api.get<SessionInfoResponse>('/api/auth/session-info', { signal: config?.signal });\r\n },\r\n};\r\n","import { useState, useEffect, useCallback, useRef } from 'react';\r\nimport { authApi, type SessionInfoResponse } from '@/services/api/authApi';\r\n\r\ninterface SessionState {\r\n isActive: boolean;\r\n timeRemainingSeconds: number;\r\n isWarning: boolean;\r\n sessionInfo: SessionInfoResponse | null;\r\n}\r\n\r\ninterface UseSessionMonitorOptions {\r\n checkIntervalMs?: number;\r\n onSessionExpired?: () => void;\r\n onWarningThreshold?: () => void;\r\n enabled?: boolean;\r\n}\r\n\r\nexport function useSessionMonitor(options: UseSessionMonitorOptions = {}): SessionState & {\n isRefreshing: boolean;\n refreshSession: () => Promise<boolean>;\n checkSession: (signal?: AbortSignal) => Promise<void>;\n} {\r\n const {\r\n checkIntervalMs = 60000,\r\n onSessionExpired,\r\n onWarningThreshold,\r\n enabled = true,\r\n } = options;\r\n\r\n const [sessionState, setSessionState] = useState<SessionState>({\r\n isActive: true,\r\n timeRemainingSeconds: 0,\r\n isWarning: false,\r\n sessionInfo: null,\r\n });\r\n\r\n const [isRefreshing, setIsRefreshing] = useState(false);\r\n const warningShownRef = useRef(false);\r\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\r\n const abortControllerRef = useRef<AbortController | null>(null);\r\n\r\n const checkSession = useCallback(async (signal?: AbortSignal) => {\r\n if (!enabled) return;\r\n\r\n const token = localStorage.getItem('token');\r\n if (!token) {\r\n setSessionState(prev => ({ ...prev, isActive: false }));\r\n return;\r\n }\r\n\r\n try {\r\n const info = await authApi.getSessionInfo({ signal });\r\n\r\n // Don't update state if aborted\r\n if (signal?.aborted) return;\r\n\r\n const minTimeRemaining = Math.min(\r\n info.timeUntilIdleExpirySeconds,\r\n info.timeUntilAbsoluteExpirySeconds\r\n );\r\n\r\n setSessionState({\r\n isActive: true,\r\n timeRemainingSeconds: minTimeRemaining,\r\n isWarning: info.isWarningThreshold,\r\n sessionInfo: info,\r\n });\r\n\r\n if (info.isWarningThreshold && !warningShownRef.current) {\r\n warningShownRef.current = true;\r\n onWarningThreshold?.();\r\n }\r\n\r\n if (!info.isWarningThreshold) {\r\n warningShownRef.current = false;\r\n }\r\n } catch (err) {\r\n // Ignore abort errors (expected in StrictMode or component unmount)\r\n if (err instanceof Error && err.name === 'CanceledError') return;\r\n if (signal?.aborted) return;\r\n\r\n setSessionState(prev => ({ ...prev, isActive: false }));\r\n onSessionExpired?.();\r\n }\r\n }, [enabled, onSessionExpired, onWarningThreshold]);\r\n\r\n const refreshSession = useCallback(async () => {\r\n if (isRefreshing) return false;\r\n\r\n setIsRefreshing(true);\r\n try {\r\n await authApi.refreshSession();\r\n warningShownRef.current = false;\r\n await checkSession();\r\n return true;\r\n } catch {\r\n setSessionState(prev => ({ ...prev, isActive: false }));\r\n return false;\r\n } finally {\r\n setIsRefreshing(false);\r\n }\r\n }, [isRefreshing, checkSession]);\r\n\r\n useEffect(() => {\r\n if (!enabled) return;\r\n\r\n // Abort previous request and create new controller\r\n abortControllerRef.current?.abort();\r\n const controller = new AbortController();\r\n abortControllerRef.current = controller;\r\n\r\n checkSession(controller.signal);\r\n\r\n // Interval checks don't need abort (they run repeatedly)\r\n intervalRef.current = setInterval(() => checkSession(), checkIntervalMs);\r\n\r\n return () => {\r\n controller.abort();\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n };\r\n }, [enabled, checkIntervalMs, checkSession]);\r\n\r\n useEffect(() => {\r\n if (!enabled) return;\r\n\r\n const handleActivity = () => {\r\n if (sessionState.isWarning) {\r\n checkSession();\r\n }\r\n };\r\n\r\n const events = ['mousedown', 'keydown', 'touchstart', 'scroll'];\r\n events.forEach(event => {\r\n window.addEventListener(event, handleActivity, { passive: true });\r\n });\r\n\r\n return () => {\r\n events.forEach(event => {\r\n window.removeEventListener(event, handleActivity);\r\n });\r\n };\r\n }, [enabled, sessionState.isWarning, checkSession]);\r\n\r\n return {\r\n ...sessionState,\r\n isRefreshing,\r\n refreshSession,\r\n checkSession,\r\n };\r\n}\r\n\r\nexport function formatTimeRemaining(seconds: number): string {\r\n if (seconds <= 0) return '0:00';\r\n\r\n const minutes = Math.floor(seconds / 60);\r\n const remainingSeconds = seconds % 60;\r\n\r\n if (minutes >= 60) {\r\n const hours = Math.floor(minutes / 60);\r\n const remainingMinutes = minutes % 60;\r\n return `${hours}h ${remainingMinutes}min`;\r\n }\r\n\r\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;\r\n}\r\n","import { useState, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Clock, LogOut, RefreshCw } from 'lucide-react';\r\nimport { formatTimeRemaining } from '@/hooks/useSessionMonitor';\r\n\r\ninterface SessionWarningModalProps {\r\n readonly isOpen: boolean;\r\n readonly timeRemainingSeconds: number;\r\n readonly onExtendSession: () => Promise<boolean>;\r\n readonly onLogout: () => void;\r\n}\r\n\r\nexport function SessionWarningModal({\r\n isOpen,\r\n timeRemainingSeconds,\r\n onExtendSession,\r\n onLogout,\r\n}: SessionWarningModalProps): ReactElement | null {\r\n const { t } = useTranslation('common');\r\n const [isExtending, setIsExtending] = useState(false);\r\n const [localTimeRemaining, setLocalTimeRemaining] = useState(timeRemainingSeconds);\r\n\r\n useEffect(() => {\r\n setLocalTimeRemaining(timeRemainingSeconds);\r\n }, [timeRemainingSeconds]);\r\n\r\n useEffect(() => {\r\n if (!isOpen || localTimeRemaining <= 0) return;\r\n\r\n const interval = setInterval(() => {\r\n setLocalTimeRemaining(prev => {\r\n if (prev <= 1) {\r\n onLogout();\r\n return 0;\r\n }\r\n return prev - 1;\r\n });\r\n }, 1000);\r\n\r\n return () => clearInterval(interval);\r\n }, [isOpen, localTimeRemaining, onLogout]);\r\n\r\n const handleExtend = async () => {\r\n setIsExtending(true);\r\n try {\r\n const success = await onExtendSession();\r\n if (!success) {\r\n onLogout();\r\n }\r\n } finally {\r\n setIsExtending(false);\r\n }\r\n };\r\n\r\n if (!isOpen) return null;\r\n\r\n const progressPercent = Math.max(0, Math.min(100, (localTimeRemaining / 300) * 100));\r\n\r\n return (\r\n <div className=\"fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm\">\r\n <div className=\"relative w-full max-w-md bg-[var(--bg-card)] rounded-2xl shadow-2xl border border-amber-500/30 overflow-hidden animate-in fade-in zoom-in duration-200\">\r\n <div className=\"absolute top-0 left-0 h-1 bg-amber-500/30 w-full\">\r\n <div\r\n className=\"h-full bg-gradient-to-r from-amber-500 to-orange-500 transition-all duration-1000 ease-linear\"\r\n style={{ width: `${progressPercent}%` }}\r\n />\r\n </div>\r\n\r\n <div className=\"p-6 pt-8\">\r\n <div className=\"flex flex-col items-center text-center mb-6\">\r\n <div className=\"w-16 h-16 rounded-full bg-amber-500/20 flex items-center justify-center mb-4\">\r\n <Clock className=\"w-8 h-8 text-amber-500\" />\r\n </div>\r\n\r\n <h2 className=\"text-xl font-semibold text-[var(--text-primary)] mb-2\">\r\n {t('session.warningTitle', 'Session Expiring')}\r\n </h2>\r\n\r\n <p className=\"text-[var(--text-secondary)] mb-4\">\r\n {t('session.warningMessage', 'Your session will expire due to inactivity.')}\r\n </p>\r\n\r\n <div className=\"text-4xl font-mono font-bold text-amber-500 tabular-nums\">\r\n {formatTimeRemaining(localTimeRemaining)}\r\n </div>\r\n\r\n <p className=\"text-sm text-[var(--text-secondary)] mt-2\">\r\n {t('session.remainingTime', 'remaining')}\r\n </p>\r\n </div>\r\n\r\n <div className=\"flex flex-col sm:flex-row gap-3\">\r\n <button\r\n onClick={onLogout}\r\n className=\"flex-1 flex items-center justify-center gap-2 px-6 py-3 rounded-xl border border-[var(--border-color)] text-[var(--text-secondary)] hover:bg-[var(--bg-hover)] transition-all font-medium\"\r\n >\r\n <LogOut className=\"w-4 h-4\" />\r\n {t('session.logout', 'Logout')}\r\n </button>\r\n\r\n <button\r\n onClick={handleExtend}\r\n disabled={isExtending}\r\n className=\"flex-1 flex items-center justify-center gap-2 px-6 py-3 rounded-xl font-medium transition-all bg-gradient-to-r from-amber-500 to-orange-500 text-white hover:from-amber-600 hover:to-orange-600 shadow-lg shadow-amber-500/30 disabled:opacity-50 disabled:cursor-not-allowed\"\r\n >\r\n <RefreshCw className={`w-4 h-4 ${isExtending ? 'animate-spin' : ''}`} />\r\n {isExtending\r\n ? t('session.extending', 'Extending...')\r\n : t('session.extend', 'Continue Session')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { createContext, useContext, useCallback, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useSessionMonitor } from '@/hooks/useSessionMonitor';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { SessionWarningModal } from './SessionWarningModal';\r\nimport { authApi } from '@/services/api/authApi';\r\n\r\ninterface SessionContextType {\r\n isSessionActive: boolean;\r\n timeRemainingSeconds: number;\r\n isWarning: boolean;\r\n refreshSession: () => Promise<boolean>;\r\n}\r\n\r\nconst SessionContext = createContext<SessionContextType | undefined>(undefined);\r\n\r\nexport function SessionProvider({ children }: { children: ReactNode }): ReactElement {\r\n const { isAuthenticated, logout } = useAuth();\r\n\r\n const handleSessionExpired = useCallback(() => {\r\n localStorage.removeItem('token');\r\n localStorage.removeItem('refreshToken');\r\n localStorage.removeItem('user');\r\n window.location.href = '/login?reason=session_expired';\r\n }, []);\r\n\r\n const {\r\n isActive,\r\n timeRemainingSeconds,\r\n isWarning,\r\n refreshSession,\r\n } = useSessionMonitor({\r\n enabled: isAuthenticated,\r\n checkIntervalMs: 30000,\r\n onSessionExpired: handleSessionExpired,\r\n });\r\n\r\n const handleLogout = useCallback(async () => {\r\n try {\r\n await authApi.logout();\r\n } finally {\r\n logout();\r\n }\r\n }, [logout]);\r\n\r\n const handleExtendSession = useCallback(async () => {\r\n const success = await refreshSession();\r\n return success;\r\n }, [refreshSession]);\r\n\r\n return (\r\n <SessionContext.Provider\r\n value={{\r\n isSessionActive: isActive,\r\n timeRemainingSeconds,\r\n isWarning,\r\n refreshSession,\r\n }}\r\n >\r\n {children}\r\n <SessionWarningModal\r\n isOpen={isWarning && isAuthenticated}\r\n timeRemainingSeconds={timeRemainingSeconds}\r\n onExtendSession={handleExtendSession}\r\n onLogout={handleLogout}\r\n />\r\n </SessionContext.Provider>\r\n );\r\n}\r\n\r\nexport function useSession(): SessionContextType {\r\n const context = useContext(SessionContext);\r\n if (context === undefined) {\r\n throw new Error('useSession must be used within a SessionProvider');\r\n }\r\n return context;\r\n}\r\n","import { useEffect, useRef, useCallback } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useAuthSafe } from '@/contexts/AuthContext';\r\nimport { useTheme } from '@/contexts/ThemeContext';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\n\r\n/** Stabilization delay to prevent rapid tenant switches from triggering multiple theme loads */\r\nconst TENANT_STABILIZATION_MS = 150;\r\n\r\nexport function ThemeSync({ children }: { children: React.ReactNode }): ReactElement {\r\n const authContext = useAuthSafe();\r\n const isAuthenticated = authContext?.isAuthenticated ?? false;\r\n const authLoading = authContext?.isLoading ?? true;\r\n const { currentTenant, isLoading: tenantLoading, isGlobalView } = useTenant();\r\n const { setSelectedThemeTenant, loadFromServer } = useTheme();\r\n // Track which tenant's preferences are currently loaded\r\n // 'global' means we've loaded global preferences\r\n const lastTenantIdRef = useRef<string | null | 'global'>(null);\r\n // Track if we've done the initial load (to distinguish login from tenant switch)\r\n const hasInitialLoadRef = useRef(false);\r\n // Stabilization: wait for rapid tenant changes to settle before loading theme\r\n const stabilizationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n // Track the tenant we're waiting to stabilize\r\n const pendingTenantIdRef = useRef<string | null>(null);\r\n\r\n /** Cancel any pending stabilization timeout */\r\n const cancelPendingTimeout = useCallback(() => {\r\n if (stabilizationTimeoutRef.current) {\r\n clearTimeout(stabilizationTimeoutRef.current);\r\n stabilizationTimeoutRef.current = null;\r\n }\r\n }, []);\r\n\r\n /** Handle entering global view mode */\r\n const handleGlobalView = useCallback(() => {\r\n const wasInGlobalView = lastTenantIdRef.current === 'global';\r\n if (wasInGlobalView) return false;\r\n\r\n cancelPendingTimeout();\r\n pendingTenantIdRef.current = null;\r\n\r\n lastTenantIdRef.current = 'global';\r\n hasInitialLoadRef.current = true;\r\n loadFromServer('global');\r\n setSelectedThemeTenant(null, true);\r\n return true;\r\n }, [cancelPendingTimeout, loadFromServer, setSelectedThemeTenant]);\r\n\r\n /** Start stabilization timer for tenant change */\r\n const startStabilizationTimer = useCallback((tenantId: string, _tenantSlug: string) => {\r\n pendingTenantIdRef.current = tenantId;\r\n\r\n stabilizationTimeoutRef.current = setTimeout(() => {\r\n if (pendingTenantIdRef.current === tenantId) {\r\n lastTenantIdRef.current = tenantId;\r\n hasInitialLoadRef.current = true;\r\n pendingTenantIdRef.current = null;\r\n setSelectedThemeTenant(tenantId, false);\r\n }\r\n stabilizationTimeoutRef.current = null;\r\n }, TENANT_STABILIZATION_MS);\r\n }, [setSelectedThemeTenant]);\r\n\r\n /** Handle tenant change */\r\n const handleTenantChange = useCallback(() => {\r\n if (!currentTenant) return;\r\n\r\n const tenantChanged = currentTenant.id !== lastTenantIdRef.current;\r\n const hasPendingDifferentTenant = pendingTenantIdRef.current !== null &&\r\n pendingTenantIdRef.current !== currentTenant.id;\r\n\r\n if (!tenantChanged && !hasPendingDifferentTenant) return;\r\n\r\n cancelPendingTimeout();\r\n\r\n if (tenantChanged) {\r\n startStabilizationTimer(currentTenant.id, currentTenant.slug);\r\n } else {\r\n pendingTenantIdRef.current = null;\r\n }\r\n }, [currentTenant, cancelPendingTimeout, startStabilizationTimer]);\r\n\r\n /** Handle user logout */\r\n const handleLogout = useCallback(() => {\r\n cancelPendingTimeout();\r\n lastTenantIdRef.current = null;\r\n pendingTenantIdRef.current = null;\r\n hasInitialLoadRef.current = false;\r\n }, [cancelPendingTimeout]);\r\n\r\n useEffect(() => {\r\n // Handle logout\r\n if (!isAuthenticated) {\r\n handleLogout();\r\n return;\r\n }\r\n\r\n // Wait for loading to complete\r\n if (authLoading || tenantLoading) return;\r\n\r\n // Handle Global view mode\r\n if (isGlobalView && !currentTenant) {\r\n handleGlobalView();\r\n return;\r\n }\r\n\r\n // Handle tenant change\r\n if (currentTenant) {\r\n handleTenantChange();\r\n }\r\n // eslint-disable-next-line react-hooks/exhaustive-deps -- selectedThemeTenant excluded to prevent circular re-renders (only used for debug logging)\r\n }, [isAuthenticated, authLoading, tenantLoading, currentTenant, isGlobalView, handleGlobalView, handleTenantChange, handleLogout]);\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n cancelPendingTimeout();\r\n };\r\n }, [cancelPendingTimeout]);\r\n\r\n return <>{children}</>;\r\n}\r\n","import { useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { AlertTriangle, RefreshCw, Home, Copy, Check, Bug, WifiOff, ShieldX, FileWarning, ArrowLeft } from 'lucide-react';\r\n\r\ninterface ErrorFallbackProps {\r\n readonly error: Error;\r\n readonly resetErrorBoundary: () => void;\r\n}\r\n\r\ntype ErrorCategory = 'network' | 'permission' | 'notFound' | 'validation' | 'technical';\r\n\r\nfunction categorizeError(error: Error): ErrorCategory {\r\n const message = error.message.toLowerCase();\r\n const name = error.name.toLowerCase();\r\n\r\n if (message.includes('network') || message.includes('fetch') || message.includes('failed to fetch') || message.includes('econnrefused') || message.includes('timeout')) {\r\n return 'network';\r\n }\r\n\r\n if (message.includes('401') || message.includes('403') || message.includes('unauthorized') || message.includes('forbidden') || message.includes('permission')) {\r\n return 'permission';\r\n }\r\n\r\n if (message.includes('404') || message.includes('not found')) {\r\n return 'notFound';\r\n }\r\n\r\n if (name.includes('validation') || message.includes('validation') || message.includes('invalid')) {\r\n return 'validation';\r\n }\r\n\r\n return 'technical';\r\n}\r\n\r\nfunction generateErrorId(): string {\r\n const timestamp = Date.now().toString(36);\r\n const random = Math.random().toString(36).substring(2, 6);\r\n return `ERR-${timestamp}-${random}`.toUpperCase();\r\n}\r\n\r\nconst categoryConfig: Record<ErrorCategory, { icon: typeof AlertTriangle; color: string; bgColor: string }> = {\r\n network: { icon: WifiOff, color: 'text-orange-500', bgColor: 'bg-orange-500/10' },\r\n permission: { icon: ShieldX, color: 'text-red-500', bgColor: 'bg-red-500/10' },\r\n notFound: { icon: FileWarning, color: 'text-yellow-500', bgColor: 'bg-yellow-500/10' },\r\n validation: { icon: AlertTriangle, color: 'text-amber-500', bgColor: 'bg-amber-500/10' },\r\n technical: { icon: Bug, color: 'text-red-500', bgColor: 'bg-red-500/10' },\r\n};\r\n\r\nexport function ErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps): ReactElement {\r\n const { t } = useTranslation('common');\r\n const [copied, setCopied] = useState(false);\r\n const [errorId] = useState(() => generateErrorId());\r\n\r\n const category = categorizeError(error);\r\n const config = categoryConfig[category];\r\n const Icon = config.icon;\r\n\r\n const handleCopyError = async () => {\r\n const errorDetails = `\r\nError ID: ${errorId}\r\nCategory: ${category}\r\nName: ${error.name}\r\nMessage: ${error.message}\r\nStack: ${error.stack || 'N/A'}\r\nURL: ${window.location.href}\r\nTimestamp: ${new Date().toISOString()}\r\nUser Agent: ${navigator.userAgent}\r\n `.trim();\r\n\r\n try {\r\n await navigator.clipboard.writeText(errorDetails);\r\n setCopied(true);\r\n setTimeout(() => setCopied(false), 2000);\r\n } catch {\r\n console.error('Failed to copy error details');\r\n }\r\n };\r\n\r\n const handleGoBack = () => {\r\n // Navigate back with a full page reload to reset React state\r\n // This ensures the crashed component gets a fresh state\r\n if (window.history.length > 2 && document.referrer) {\r\n // Use referrer for a clean reload to the previous page\r\n window.location.href = document.referrer;\r\n } else if (window.history.length > 2) {\r\n // Fallback: go back then reload (needed when referrer is empty)\r\n window.history.back();\r\n // Small delay then reload to ensure navigation completes\r\n setTimeout(() => window.location.reload(), 50);\r\n } else {\r\n // No history available, go to home\r\n window.location.href = '/';\r\n }\r\n };\r\n\r\n return (\r\n <div className=\"min-h-screen flex items-center justify-center bg-[var(--bg-primary)] p-4\">\r\n <div className=\"max-w-lg w-full p-8 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-2xl shadow-xl\">\r\n <div className=\"flex flex-col items-center text-center\">\r\n {/* Icon */}\r\n <div className={`w-16 h-16 rounded-full ${config.bgColor} flex items-center justify-center mb-6`}>\r\n <Icon className={`w-8 h-8 ${config.color}`} />\r\n </div>\r\n\r\n {/* Title */}\r\n <h1 className=\"text-2xl font-bold text-[var(--text-primary)] mb-2\">\r\n {t(`errors.${category}.title`, { defaultValue: t('errors.technical.title') })}\r\n </h1>\r\n\r\n {/* Description */}\r\n <p className=\"text-[var(--text-secondary)] mb-4\">\r\n {t(`errors.${category}.description`, { defaultValue: t('errors.technical.description') })}\r\n </p>\r\n\r\n {/* Error ID */}\r\n <div className=\"mb-6 px-3 py-1.5 bg-[var(--bg-tertiary)] rounded-lg border border-[var(--border-color)]\">\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">{t('errors.errorId')}: </span>\r\n <span className=\"text-xs font-mono text-[var(--text-secondary)]\">{errorId}</span>\r\n </div>\r\n\r\n {/* Dev Mode: Error Details */}\r\n {import.meta.env.DEV && (\r\n <div className=\"w-full mb-6 p-4 bg-red-500/10 border border-red-500/20 rounded-xl text-left\">\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <span className=\"text-sm font-semibold text-red-400\">{error.name}</span>\r\n <button\r\n onClick={handleCopyError}\r\n className=\"flex items-center gap-1 px-2 py-1 text-xs rounded-md bg-red-500/20 text-red-400 hover:bg-red-500/30 transition-colors\"\r\n >\r\n {copied ? (\r\n <>\r\n <Check className=\"w-3 h-3\" />\r\n {t('errors.copied')}\r\n </>\r\n ) : (\r\n <>\r\n <Copy className=\"w-3 h-3\" />\r\n {t('errors.copyDetails')}\r\n </>\r\n )}\r\n </button>\r\n </div>\r\n <p className=\"text-sm font-mono text-red-500 break-all\">\r\n {error.message}\r\n </p>\r\n {error.stack && (\r\n <pre className=\"mt-2 text-xs text-red-400 overflow-auto max-h-32 scrollbar-thin\">\r\n {error.stack}\r\n </pre>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Actions */}\r\n <div className=\"flex flex-col sm:flex-row gap-3 w-full\">\r\n <button\r\n onClick={resetErrorBoundary}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--color-accent-600)] hover:bg-[var(--color-accent-700)] text-white font-medium transition-all\"\r\n >\r\n <RefreshCw className=\"w-4 h-4\" />\r\n {t('errors.retry')}\r\n </button>\r\n\r\n <button\r\n onClick={handleGoBack}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-hover)] border border-[var(--border-color)] text-[var(--text-primary)] font-medium transition-all\"\r\n >\r\n <ArrowLeft className=\"w-4 h-4\" />\r\n {t('errors.goBack')}\r\n </button>\r\n\r\n <a\r\n href=\"/\"\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-hover)] border border-[var(--border-color)] text-[var(--text-primary)] font-medium transition-all\"\r\n >\r\n <Home className=\"w-4 h-4\" />\r\n {t('errors.home')}\r\n </a>\r\n </div>\r\n\r\n {/* Help Link */}\r\n <p className=\"mt-6 text-sm text-[var(--text-tertiary)]\">\r\n {t('errors.persistsHelp')}{' '}\r\n <a\r\n href=\"mailto:support@atlashub.ch\"\r\n className=\"text-[var(--color-accent-500)] hover:underline\"\r\n >\r\n {t('errors.contactSupport')}\r\n </a>\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { ErrorBoundary } from 'react-error-boundary';\r\nimport { ErrorFallback } from './ErrorFallback';\r\nimport { logService } from '@/services/logging/logService';\r\n\r\ninterface GlobalErrorBoundaryProps {\r\n readonly children: React.ReactNode;\r\n}\r\n\r\nfunction handleError(error: Error, info: React.ErrorInfo) {\r\n logService.logCritical(error, 'GlobalErrorBoundary', {\r\n componentStack: info.componentStack\r\n });\r\n}\r\n\r\nfunction handleReset() {\r\n // Reload current page to reset React state and give the user another chance\r\n window.location.reload();\r\n}\r\n\r\nexport function GlobalErrorBoundary({ children }: GlobalErrorBoundaryProps): ReactElement {\r\n return (\r\n <ErrorBoundary\r\n FallbackComponent={ErrorFallback}\r\n onError={handleError}\r\n onReset={handleReset}\r\n >\r\n {children}\r\n </ErrorBoundary>\r\n );\r\n}\r\n","import { createContext,\r\n useContext,\r\n useMemo,\r\n type ReactNode,\r\n type ComponentType,\r\n isValidElement,\r\n Fragment, type ReactElement } from 'react';\r\nimport type { SlotContent, SlotProps } from './types';\r\n\r\n/**\r\n * SlotFill Context - manages slot registrations\r\n */\r\ninterface SlotFillContextValue {\r\n fills: Map<string, SlotContent[]>;\r\n registerFill: (slotName: string, content: SlotContent) => void;\r\n unregisterFill: (slotName: string, content: SlotContent) => void;\r\n getSlotContent: (slotName: string) => SlotContent[];\r\n}\r\n\r\nconst SlotFillContext = createContext<SlotFillContextValue | null>(null);\r\n\r\n/**\r\n * Hook to access SlotFill context\r\n */\r\nexport function useSlotFill(): SlotFillContextValue {\r\n const context = useContext(SlotFillContext);\r\n if (!context) {\r\n throw new Error('useSlotFill must be used within a SlotFillProvider');\r\n }\r\n return context;\r\n}\r\n\r\n/**\r\n * SlotFillProvider - Wrap your app to enable slot/fill functionality\r\n */\r\ninterface SlotFillProviderProps {\r\n readonly children: ReactNode;\r\n readonly initialFills?: Record<string, SlotContent>;\r\n}\r\n\r\nexport function SlotFillProvider({ children, initialFills }: SlotFillProviderProps): ReactElement {\r\n const fills = useMemo(() => {\r\n const map = new Map<string, SlotContent[]>();\r\n if (initialFills) {\r\n Object.entries(initialFills).forEach(([slotName, content]) => {\r\n const existing = map.get(slotName) || [];\r\n if (Array.isArray(content)) {\r\n map.set(slotName, [...existing, ...content]);\r\n } else {\r\n map.set(slotName, [...existing, content]);\r\n }\r\n });\r\n }\r\n return map;\r\n }, [initialFills]);\r\n\r\n const contextValue = useMemo<SlotFillContextValue>(\r\n () => ({\r\n fills,\r\n registerFill: (slotName: string, content: SlotContent) => {\r\n const existing = fills.get(slotName) || [];\r\n fills.set(slotName, [...existing, content]);\r\n },\r\n unregisterFill: (slotName: string, content: SlotContent) => {\r\n const existing = fills.get(slotName) || [];\r\n fills.set(\r\n slotName,\r\n existing.filter((c) => c !== content)\r\n );\r\n },\r\n getSlotContent: (slotName: string) => fills.get(slotName) || [],\r\n }),\r\n [fills]\r\n );\r\n\r\n return <SlotFillContext.Provider value={contextValue}>{children}</SlotFillContext.Provider>;\r\n}\r\n\r\n/**\r\n * Slot - Renders content injected by Fill components or extensions\r\n */\r\ninterface SlotComponentProps {\r\n readonly name: string;\r\n readonly context?: Record<string, unknown>;\r\n readonly children?: ReactNode;\r\n readonly fallback?: ReactNode;\r\n readonly wrapper?: ComponentType<{ children: ReactNode }>;\r\n}\r\n\r\nexport function Slot({ name, context = {}, children, fallback, wrapper: Wrapper }: SlotComponentProps): ReactElement | null {\r\n const slotFill = useContext(SlotFillContext);\r\n const slotContent = slotFill?.getSlotContent(name) || [];\r\n\r\n const props: SlotProps = useMemo(\r\n () => ({\r\n slotName: name,\r\n context,\r\n }),\r\n [name, context]\r\n );\r\n\r\n const renderedContent = useMemo(() => {\r\n if (slotContent.length === 0) {\r\n return fallback || null;\r\n }\r\n\r\n return slotContent.map((content, index) => {\r\n const key = `${name}-${index}`;\r\n return (\r\n <Fragment key={key}>\r\n {renderSlotContent(content, props)}\r\n </Fragment>\r\n );\r\n });\r\n }, [slotContent, name, props, fallback]);\r\n\r\n const finalContent = (\r\n <>\r\n {renderedContent}\r\n {children}\r\n </>\r\n );\r\n\r\n if (Wrapper) {\r\n return <Wrapper>{finalContent}</Wrapper>;\r\n }\r\n\r\n return <>{finalContent}</>;\r\n}\r\n\r\n/**\r\n * Fill - Injects content into a named Slot\r\n */\r\ninterface FillProps {\r\n readonly slot: string;\r\n readonly children: ReactNode;\r\n}\r\n\r\nexport function Fill({ slot, children }: FillProps): ReactElement | null {\r\n const { registerFill, unregisterFill } = useSlotFill();\r\n\r\n useMemo(() => {\r\n registerFill(slot, children);\r\n return () => unregisterFill(slot, children);\r\n }, [slot, children, registerFill, unregisterFill]);\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Helper to render slot content based on its type\r\n */\r\nfunction renderSlotContent(content: SlotContent, props: SlotProps): ReactNode {\r\n if (content === null || content === undefined) {\r\n return null;\r\n }\r\n\r\n if (Array.isArray(content)) {\r\n return (\r\n <>\r\n {content.map((item, index) => (\r\n <Fragment key={`slot-item-${index}`}>{renderSlotContent(item, props)}</Fragment>\r\n ))}\r\n </>\r\n );\r\n }\r\n\r\n if (isValidElement(content)) {\r\n return content;\r\n }\r\n\r\n if (typeof content === 'function') {\r\n const Component = content as ComponentType<SlotProps>;\r\n return <Component {...props} />;\r\n }\r\n\r\n return content as ReactNode;\r\n}\r\n\r\n/**\r\n * Hook to check if a slot has any content\r\n */\r\nexport function useSlotHasContent(slotName: string): boolean {\r\n const slotFill = useContext(SlotFillContext);\r\n return (slotFill?.getSlotContent(slotName)?.length || 0) > 0;\r\n}\r\n\r\n/**\r\n * createSlotFill - Factory to create typed Slot/Fill pairs\r\n */\r\nexport function createSlotFill<T extends Record<string, unknown> = Record<string, unknown>>(slotName: string) {\r\n function TypedSlot(props: Omit<SlotComponentProps, 'name'> & { context?: T }) {\r\n return <Slot {...props} name={slotName} />;\r\n }\r\n TypedSlot.displayName = `Slot(${slotName})`;\r\n\r\n function TypedFill({ children }: { children: ReactNode }) {\r\n return <Fill slot={slotName} children={children} />;\r\n }\r\n TypedFill.displayName = `Fill(${slotName})`;\r\n\r\n return {\r\n Slot: TypedSlot,\r\n Fill: TypedFill,\r\n slotName,\r\n };\r\n}\r\n","import { createContext, useContext, useMemo, type ReactNode, type ComponentType } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport type {\r\n ExtensionConfig,\r\n ExtensionContextValue,\r\n PageProps,\r\n SlotContent,\r\n ColumnExtension,\r\n FormFieldExtension,\r\n} from './types';\r\nimport { SlotFillProvider } from './SlotFill';\r\n\r\nconst ExtensionContext = createContext<ExtensionContextValue | null>(null);\r\n\r\n/**\r\n * Hook to access extension configuration and utilities\r\n */\r\nexport function useExtensions(): ExtensionContextValue {\r\n const context = useContext(ExtensionContext);\r\n if (!context) {\r\n return {\r\n config: {},\r\n getPage: () => undefined,\r\n getSlotContent: () => undefined,\r\n getTableColumns: () => [],\r\n getFormFields: () => [],\r\n hasExtension: () => false,\r\n };\r\n }\r\n return context;\r\n}\r\n\r\n/**\r\n * Hook to get an overridden page component\r\n */\r\nexport function usePageOverride(pageKey: string): ComponentType<PageProps> | undefined {\r\n const { getPage } = useExtensions();\r\n return getPage(pageKey);\r\n}\r\n\r\n/**\r\n * Hook to get table column extensions\r\n */\r\nexport function useTableColumnExtensions(tableId: string): ColumnExtension[] {\r\n const { getTableColumns } = useExtensions();\r\n return getTableColumns(tableId);\r\n}\r\n\r\n/**\r\n * Hook to get form field extensions\r\n */\r\nexport function useFormFieldExtensions(formId: string): FormFieldExtension[] {\r\n const { getFormFields } = useExtensions();\r\n return getFormFields(formId);\r\n}\r\n\r\n/**\r\n * Hook to check if a specific extension is configured\r\n */\r\nexport function useHasExtension(key: string): boolean {\r\n const { hasExtension } = useExtensions();\r\n return hasExtension(key);\r\n}\r\n\r\ninterface ExtensionProviderProps {\r\n readonly config?: ExtensionConfig;\r\n readonly children: ReactNode;\r\n}\r\n\r\n/**\r\n * ExtensionProvider - Provides extension configuration to the app\r\n */\r\nexport function ExtensionProvider({ config = {}, children }: ExtensionProviderProps): ReactElement {\r\n const contextValue = useMemo<ExtensionContextValue>(() => {\r\n const { pages = {}, slots = {}, tableColumns = {}, formFields = {} } = config;\r\n\r\n return {\r\n config,\r\n getPage: (key: string): ComponentType<PageProps> | undefined => {\r\n return pages[key];\r\n },\r\n getSlotContent: (slotName: string): SlotContent | undefined => {\r\n return slots[slotName];\r\n },\r\n getTableColumns: (tableId: string): ColumnExtension[] => {\r\n return tableColumns[tableId] || [];\r\n },\r\n getFormFields: (formId: string): FormFieldExtension[] => {\r\n return formFields[formId] || [];\r\n },\r\n hasExtension: (key: string): boolean => {\r\n return (\r\n key in pages ||\r\n key in slots ||\r\n key in tableColumns ||\r\n key in formFields\r\n );\r\n },\r\n };\r\n }, [config]);\r\n\r\n return (\r\n <ExtensionContext.Provider value={contextValue}>\r\n <SlotFillProvider initialFills={config.slots}>\r\n {children}\r\n </SlotFillProvider>\r\n </ExtensionContext.Provider>\r\n );\r\n}\r\n","import { type ComponentType } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport type { PageProps, PageRegistryEntry, RegisterableComponent, SlotName } from './types';\r\nimport { usePageOverride } from './ExtensionContext';\r\n\r\n/**\r\n * Page Registry - Central registry for SmartStack pages\r\n * Enables page overrides by clients and dynamic route resolution.\r\n *\r\n * Supports both regular and lazy-loaded components:\r\n * PageRegistry.register('app.module', lazy(() => import('./Page')));\r\n * PageRegistry.register('app.module', RegularComponent);\r\n */\r\nclass PageRegistryClass {\r\n private pages: Map<string, PageRegistryEntry> = new Map();\r\n\r\n /**\r\n * Register a page component (regular or lazy-loaded).\r\n * Used by SmartStack core (componentRegistry.generated.ts) and SDK clients.\r\n */\r\n register(key: string, component: RegisterableComponent, slots: SlotName[] = []): void {\r\n this.pages.set(key, {\r\n key,\r\n defaultComponent: component,\r\n slots,\r\n });\r\n }\r\n\r\n /**\r\n * Get a page entry by key\r\n */\r\n get(key: string): PageRegistryEntry | undefined {\r\n return this.pages.get(key);\r\n }\r\n\r\n /**\r\n * Resolve the component for a given key.\r\n * Returns the registered component or undefined if not found.\r\n */\r\n resolve(key: string): RegisterableComponent | undefined {\r\n return this.pages.get(key)?.defaultComponent;\r\n }\r\n\r\n /**\r\n * Get all registered pages\r\n */\r\n getAll(): PageRegistryEntry[] {\r\n return Array.from(this.pages.values());\r\n }\r\n\r\n /**\r\n * Check if a page is registered\r\n */\r\n has(key: string): boolean {\r\n return this.pages.has(key);\r\n }\r\n\r\n /**\r\n * Get available slots for a page\r\n */\r\n getSlots(key: string): SlotName[] {\r\n return this.pages.get(key)?.slots || [];\r\n }\r\n\r\n /**\r\n * Get all registered keys (useful for validation)\r\n */\r\n keys(): string[] {\r\n return Array.from(this.pages.keys());\r\n }\r\n}\r\n\r\nexport const PageRegistry = new PageRegistryClass();\r\n\r\n/**\r\n * Higher-order component that enables page override\r\n * Wraps a page component to check for client override\r\n */\r\nexport function withPageOverride<P extends PageProps>(\r\n pageKey: string,\r\n DefaultComponent: ComponentType<P>\r\n): ComponentType<P> {\r\n function OverridablePage(props: P) {\r\n const OverrideComponent = usePageOverride(pageKey);\r\n\r\n if (OverrideComponent) {\r\n return <OverrideComponent {...props} />;\r\n }\r\n\r\n return <DefaultComponent {...props} />;\r\n }\r\n\r\n OverridablePage.displayName = `Overridable(${pageKey})`;\r\n return OverridablePage;\r\n}\r\n\r\n/**\r\n * Hook to get the active page component (override or default)\r\n */\r\nexport function useActivePage(pageKey: string): RegisterableComponent | undefined {\r\n const override = usePageOverride(pageKey);\r\n const entry = PageRegistry.get(pageKey);\r\n\r\n return override || entry?.defaultComponent;\r\n}\r\n\r\n/**\r\n * Component that renders an overridable page\r\n */\r\ninterface ExtendablePageProps {\r\n readonly pageKey: string;\r\n readonly defaultComponent: ComponentType<PageProps>;\r\n readonly params?: Record<string, string>;\r\n}\r\n\r\nexport function ExtendablePage({ pageKey, defaultComponent: DefaultComponent, params }: ExtendablePageProps): ReactElement {\r\n const OverrideComponent = usePageOverride(pageKey);\r\n const props: PageProps = { pageKey, params };\r\n\r\n if (OverrideComponent) {\r\n return <OverrideComponent {...props} />;\r\n }\r\n\r\n return <DefaultComponent {...props} />;\r\n}\r\n\r\n/**\r\n * Pre-defined page keys for SmartStack pages.\r\n * Convention: hierarchical code path matching backend componentKey.\r\n *\r\n * Implicit route suffixes (not in DB — auto-generated by DynamicRouter):\r\n *\r\n * Entity sub-pages (require :id):\r\n * *.detail → /:id\r\n * *.edit → /:id/edit\r\n * *.duplicate → /:id/duplicate\r\n * *.settings → /:id/settings\r\n * *.configure → /:id/configure\r\n * *.permissions → /:id/permissions\r\n * *.members → /:id/members\r\n * *.history → /:id/history\r\n * *.logs → /:id/logs\r\n * *.analytics → /:id/analytics\r\n * *.preview → /:id/preview\r\n * *.versions → /:id/versions\r\n * *.comments → /:id/comments\r\n * *.attachments → /:id/attachments\r\n * *.audit → /:id/audit\r\n * *.export → /:id/export\r\n * *.notifications → /:id/notifications\r\n * *.schedule → /:id/schedule\r\n * *.workflow → /:id/workflow\r\n * *.summary → /:id/summary\r\n * *.test → /:id/test\r\n * *.runs → /:id/runs\r\n *\r\n * Collection-level pages (no :id):\r\n * *.create → /create\r\n * *.new → /new\r\n * *.import → /import\r\n */\r\nexport const PAGE_KEYS = {\r\n // ── Administration ──\r\n ADMIN_INDEX: 'administration',\r\n ADMIN_DASHBOARD: 'administration.dashboard',\r\n\r\n // Users module\r\n USERS_LIST: 'administration.users.list',\r\n USERS_DETAIL: 'administration.users.detail',\r\n USERS_DASHBOARD: 'administration.users.dashboard',\r\n USERS_GROUPS: 'administration.users.groups',\r\n USERS_GROUPS_DETAIL: 'administration.users.groups.detail',\r\n USERS_REFERENCES: 'administration.users.references',\r\n\r\n // Permissions module\r\n ROLES_LIST: 'administration.permissions.roles',\r\n ROLES_DETAIL: 'administration.permissions.roles.detail',\r\n PERMISSIONS_LIST: 'administration.permissions.permissions',\r\n PERMISSIONS_DETAIL: 'administration.permissions.permissions.detail',\r\n ASSIGNMENTS: 'administration.permissions.assignments',\r\n\r\n // Applications module\r\n APPLICATIONS_INDEX: 'administration.applications',\r\n APPLICATIONS_DASHBOARD: 'administration.applications.dashboard',\r\n APPLICATIONS_LIST: 'administration.applications.list',\r\n APPLICATIONS_DETAIL: 'administration.applications.detail',\r\n\r\n // AI module\r\n AI_DASHBOARD: 'administration.ai.dashboard',\r\n AI_SETTINGS: 'administration.ai.settings',\r\n AI_SKILLS_LIST: 'administration.ai.skills',\r\n AI_SKILLS_DETAIL: 'administration.ai.skills.detail',\r\n AI_SKILLS_CREATE: 'administration.ai.skills.create',\r\n AI_SKILLS_EDIT: 'administration.ai.skills.edit',\r\n AI_BLOCKS_LIST: 'administration.ai.skills.blocks',\r\n AI_BLOCKS_DETAIL: 'administration.ai.skills.blocks.detail',\r\n AI_BLOCKS_CREATE: 'administration.ai.skills.blocks.create',\r\n AI_BLOCKS_EDIT: 'administration.ai.skills.blocks.edit',\r\n AI_MONITORING: 'administration.ai.monitoring',\r\n AI_AGENTS: 'administration.ai.agents',\r\n AI_AGENTS_DETAIL: 'administration.ai.agents.detail',\r\n AI_AGENTS_EDIT: 'administration.ai.agents.edit',\r\n\r\n // Workflows module\r\n WORKFLOWS_LIST: 'administration.workflows.list',\r\n WORKFLOWS_DETAIL: 'administration.workflows.detail',\r\n WORKFLOWS_CREATE: 'administration.workflows.create',\r\n WORKFLOWS_EDIT: 'administration.workflows.edit',\r\n WORKFLOWS_TRIGGERS: 'administration.workflows.triggers',\r\n WORKFLOWS_HISTORY: 'administration.workflows.history',\r\n EMAIL_TEMPLATES_LIST: 'administration.workflows.email-templates',\r\n EMAIL_TEMPLATES_DETAIL: 'administration.workflows.email-templates.detail',\r\n EMAIL_TEMPLATES_CREATE: 'administration.workflows.email-templates.create',\r\n EMAIL_TEMPLATES_EDIT: 'administration.workflows.email-templates.edit',\r\n SMS_TEMPLATES_LIST: 'administration.workflows.email-templates.sms',\r\n\r\n // Entra module\r\n ENTRA_DASHBOARD: 'administration.entra.dashboard',\r\n ENTRA_GROUPS: 'administration.entra.groups',\r\n ENTRA_USERS: 'administration.entra.users',\r\n ENTRA_CONFLICTS: 'administration.entra.conflicts',\r\n ENTRA_SETTINGS: 'administration.entra.settings',\r\n\r\n // Tenants module\r\n TENANTS_DASHBOARD: 'administration.tenants.dashboard',\r\n TENANTS_LIST: 'administration.tenants.list',\r\n TENANTS_DETAIL: 'administration.tenants.detail',\r\n TENANTS_CREATE: 'administration.tenants.create',\r\n TENANTS_SETTINGS: 'administration.tenants.settings',\r\n TENANTS_TYPE_SETTINGS: 'administration.tenants.type-settings',\r\n TENANTS_TEMPLATE: 'administration.tenants.template',\r\n TENANTS_TEMPLATE_CREATE: 'administration.tenants.template.create',\r\n TENANTS_TEMPLATE_DETAIL: 'administration.tenants.template.detail',\r\n TENANTS_BUSINESS: 'administration.tenants.list.enterprise',\r\n TENANTS_PERSONAL: 'administration.tenants.list.personal',\r\n TENANTS_OTHER: 'administration.tenants.list.other',\r\n TENANTS_ACCESS_REQUESTS: 'administration.tenants.access-requests',\r\n ORGANISATIONS_LIST: 'administration.tenants.organisations',\r\n ORGANISATIONS_CREATE: 'administration.tenants.organisations.create',\r\n ORGANISATIONS_DETAIL: 'administration.tenants.organisations.detail',\r\n\r\n // Configuration module\r\n SETTINGS: 'administration.configuration.settings',\r\n SETTINGS_GENERAL: 'administration.configuration.settings.general',\r\n SETTINGS_FILE_UPLOAD: 'administration.configuration.settings.file-upload',\r\n SETTINGS_LEGAL_FILE: 'administration.configuration.settings.legal-file',\r\n APPSETTINGS: 'administration.configuration.appsettings',\r\n APPSETTINGS_INFRASTRUCTURE: 'administration.configuration.appsettings.infrastructure',\r\n APPSETTINGS_AUTHENTICATION: 'administration.configuration.appsettings.authentication',\r\n APPSETTINGS_LOGGING: 'administration.configuration.appsettings.logging',\r\n APPSETTINGS_EMAIL: 'administration.configuration.appsettings.email',\r\n APPSETTINGS_ENTRA: 'administration.configuration.appsettings.entra',\r\n LICENSE: 'administration.configuration.license',\r\n\r\n // ── API Application ──\r\n API_ACCOUNTS_LIST: 'api.accounts.list',\r\n API_ACCOUNTS_CREATE: 'api.accounts.create',\r\n API_ACCOUNTS_DETAIL: 'api.accounts.detail',\r\n API_CATALOG: 'api.apis.catalog',\r\n API_CATALOG_DETAIL: 'api.apis.catalog.detail',\r\n\r\n // ── Support Application ──\r\n SUPPORT_DASHBOARD: 'support.tickets',\r\n TICKETS_LIST: 'support.tickets.list',\r\n TICKETS_DETAIL: 'support.tickets.detail',\r\n TICKETS_ESCALATION: 'support.tickets.escalation',\r\n TICKETS_NEW: 'support.tickets.new',\r\n MY_TICKETS_LIST: 'support.my-tickets',\r\n MY_TICKETS_DETAIL: 'support.my-tickets.detail',\r\n MY_TICKETS_CREATE: 'support.my-tickets.create',\r\n SUPPORT_SLA: 'support.admin.sla',\r\n SUPPORT_TEMPLATES: 'support.admin.templates',\r\n SUPPORT_PERMISSIONS: 'support.admin.permissions',\r\n SUPPORT_ASSIGNMENT: 'support.assignment',\r\n SUPPORT_ASSIGNMENT_SKILLS: 'support.assignment.skills',\r\n SUPPORT_ASSIGNMENT_WORKLOAD: 'support.assignment.workload',\r\n\r\n // ── MySpace Application ──\r\n MYSPACE_DASHBOARD: 'myspace.dashboard',\r\n USER_PROFILE: 'myspace.profile',\r\n USER_PREFERENCES: 'myspace.preferences',\r\n MYSPACE_TENANTS: 'myspace.tenants.workspaces',\r\n MYSPACE_ACCESS_REQUESTS: 'myspace.tenants.access-requests',\r\n MYSPACE_PORTAL: 'myspace.portal',\r\n\r\n // ── System (open apps) ──\r\n NOTIFICATIONS: 'notifications',\r\n APPLICATIONS_HUB: 'applications',\r\n} as const;\r\n\r\nexport type PageKey = (typeof PAGE_KEYS)[keyof typeof PAGE_KEYS];\r\n","/**\r\n * Component Registry — AUTO-GENERATED by MCP scaffold_routes\r\n * DO NOT EDIT MANUALLY — regenerate with: mcp scaffold_routes\r\n *\r\n * Registers all SmartStack core pages in the PageRegistry.\r\n * This enables the DynamicRouter to resolve components from their componentKey.\r\n *\r\n * Import this file once at app startup (main.tsx or App.tsx) to populate the registry.\r\n */\r\nimport { lazy } from 'react';\r\nimport { PageRegistry } from './PageRegistry';\r\nimport { PAGE_KEYS } from './PageRegistry';\r\n\r\n// ============================================================================\r\n// ADMINISTRATION APPLICATION\r\n// ============================================================================\r\n\r\n// Index / Dashboard\r\nPageRegistry.register(PAGE_KEYS.ADMIN_INDEX, lazy(() => import('@/pages/platform/administration/applications/ApplicationsGridPage').then(m => ({ default: m.ApplicationsGridPage }))));\r\nPageRegistry.register(PAGE_KEYS.ADMIN_DASHBOARD, lazy(() => import('@/pages/platform/administration/DashboardPage').then(m => ({ default: m.AdminDashboardPage }))));\r\n\r\n// Users module\r\nPageRegistry.register(PAGE_KEYS.USERS_LIST, lazy(() => import('@/pages/platform/administration/users/UsersPage').then(m => ({ default: m.UsersPage }))));\r\nPageRegistry.register(PAGE_KEYS.USERS_DETAIL, lazy(() => import('@/pages/platform/administration/users/UserDetailPage').then(m => ({ default: m.UserDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.USERS_DASHBOARD, lazy(() => import('@/pages/platform/administration/DashboardPage').then(m => ({ default: m.AdminDashboardPage }))));\r\nPageRegistry.register(PAGE_KEYS.USERS_GROUPS, lazy(() => import('@/pages/platform/administration/users/UsersGroupsPage').then(m => ({ default: m.UsersGroupsPage }))));\r\nPageRegistry.register(PAGE_KEYS.USERS_GROUPS_DETAIL, lazy(() => import('@/pages/platform/administration/users/GroupDetailPage').then(m => ({ default: m.GroupDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.USERS_REFERENCES, lazy(() => import('@/pages/platform/administration/users/ReferencesManagementPage').then(m => ({ default: m.ReferencesManagementPage }))));\r\n\r\n// Permissions module\r\nPageRegistry.register(PAGE_KEYS.ROLES_LIST, lazy(() => import('@/pages/platform/administration/permissions/RolesPage').then(m => ({ default: m.RolesPage }))));\r\nPageRegistry.register(PAGE_KEYS.ROLES_DETAIL, lazy(() => import('@/pages/platform/administration/permissions/RoleDetailPage').then(m => ({ default: m.RoleDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.PERMISSIONS_LIST, lazy(() => import('@/pages/platform/administration/permissions/PermissionsPage').then(m => ({ default: m.PermissionsPage }))));\r\nPageRegistry.register(PAGE_KEYS.PERMISSIONS_DETAIL, lazy(() => import('@/pages/platform/administration/permissions/PermissionDetailPage').then(m => ({ default: m.PermissionDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.ASSIGNMENTS, lazy(() => import('@/pages/platform/administration/permissions/AssignmentsPage').then(m => ({ default: m.AssignmentsPage }))));\r\n\r\n// Applications module\r\nPageRegistry.register(PAGE_KEYS.APPLICATIONS_INDEX, lazy(() => import('@/pages/platform/administration/applications/NavigationAppsPage').then(m => ({ default: m.NavigationAppsPage }))));\r\nPageRegistry.register(PAGE_KEYS.APPLICATIONS_DASHBOARD, lazy(() => import('@/pages/platform/administration/applications/ApplicationsDashboardPage').then(m => ({ default: m.ApplicationsDashboardPage }))));\r\nPageRegistry.register(PAGE_KEYS.APPLICATIONS_LIST, lazy(() => import('@/pages/platform/administration/applications/ApplicationsListPage').then(m => ({ default: m.ApplicationsListPage }))));\r\nPageRegistry.register(PAGE_KEYS.APPLICATIONS_DETAIL, lazy(() => import('@/pages/platform/administration/applications/ApplicationDetailPage')));\r\n\r\n// AI module\r\nPageRegistry.register(PAGE_KEYS.AI_DASHBOARD, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiDashboardPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_SETTINGS, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiSettingsPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_SKILLS_LIST, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiSkillsListPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_SKILLS_DETAIL, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiSkillsDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_SKILLS_CREATE, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiSkillsEditPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_SKILLS_EDIT, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiSkillsEditPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_BLOCKS_LIST, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiBlockDefinitionsListPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_BLOCKS_DETAIL, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiBlockDefinitionDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_BLOCKS_CREATE, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiBlockDefinitionEditPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_BLOCKS_EDIT, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiBlockDefinitionEditPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_MONITORING, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiMonitoringPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_AGENTS, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiAgentsPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_AGENTS_DETAIL, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiAgentDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.AI_AGENTS_EDIT, lazy(() => import('@/pages/platform/administration/ai').then(m => ({ default: m.AiAgentEditPage }))));\r\n\r\n// Workflows module\r\nPageRegistry.register(PAGE_KEYS.WORKFLOWS_LIST, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.WorkflowsListPage }))));\r\nPageRegistry.register(PAGE_KEYS.WORKFLOWS_DETAIL, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.WorkflowDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.WORKFLOWS_CREATE, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.WorkflowCreatePage }))));\r\nPageRegistry.register(PAGE_KEYS.WORKFLOWS_EDIT, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.WorkflowEditPage }))));\r\nPageRegistry.register(PAGE_KEYS.WORKFLOWS_TRIGGERS, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.WorkflowsTriggersPage }))));\r\nPageRegistry.register(PAGE_KEYS.WORKFLOWS_HISTORY, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.WorkflowHistoryPage }))));\r\nPageRegistry.register(PAGE_KEYS.EMAIL_TEMPLATES_LIST, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.EmailTemplatesListPage }))));\r\nPageRegistry.register(PAGE_KEYS.EMAIL_TEMPLATES_DETAIL, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.EmailTemplateDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.EMAIL_TEMPLATES_CREATE, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.EmailTemplateCreatePage }))));\r\nPageRegistry.register(PAGE_KEYS.EMAIL_TEMPLATES_EDIT, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.EmailTemplateEditPage }))));\r\nPageRegistry.register(PAGE_KEYS.SMS_TEMPLATES_LIST, lazy(() => import('@/pages/platform/administration/workflows').then(m => ({ default: m.SmsTemplatesListPage }))));\r\n\r\n// Entra module\r\nPageRegistry.register(PAGE_KEYS.ENTRA_DASHBOARD, lazy(() => import('@/pages/platform/administration/entra').then(m => ({ default: m.EntraDashboardPage }))));\r\nPageRegistry.register(PAGE_KEYS.ENTRA_GROUPS, lazy(() => import('@/pages/platform/administration/entra').then(m => ({ default: m.EntraGroupsPage }))));\r\nPageRegistry.register(PAGE_KEYS.ENTRA_USERS, lazy(() => import('@/pages/platform/administration/entra').then(m => ({ default: m.EntraUsersPage }))));\r\nPageRegistry.register(PAGE_KEYS.ENTRA_CONFLICTS, lazy(() => import('@/pages/platform/administration/entra').then(m => ({ default: m.EntraConflictsPage }))));\r\nPageRegistry.register(PAGE_KEYS.ENTRA_SETTINGS, lazy(() => import('@/pages/platform/administration/entra').then(m => ({ default: m.EntraSettingsPage }))));\r\n\r\n// Tenants module\r\nPageRegistry.register(PAGE_KEYS.TENANTS_DASHBOARD, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantsDashboardPage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_LIST, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantsPage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_DETAIL, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_CREATE, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantCreatePage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_SETTINGS, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantSettingsPage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_TYPE_SETTINGS, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantTypeSettingsPage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_TEMPLATE, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantsTemplatePage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_TEMPLATE_CREATE, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantTemplateCreatePage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_TEMPLATE_DETAIL, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantTemplateDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_BUSINESS, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantsBusinessPage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_PERSONAL, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantsPersonalPage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_OTHER, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantsOtherPage }))));\r\nPageRegistry.register(PAGE_KEYS.TENANTS_ACCESS_REQUESTS, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.TenantAccessRequestsPage }))));\r\nPageRegistry.register(PAGE_KEYS.ORGANISATIONS_LIST, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.OrganisationsListPage }))));\r\nPageRegistry.register(PAGE_KEYS.ORGANISATIONS_CREATE, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.OrganisationCreatePage }))));\r\nPageRegistry.register(PAGE_KEYS.ORGANISATIONS_DETAIL, lazy(() => import('@/pages/platform/administration/tenants').then(m => ({ default: m.OrganisationDetailPage }))));\r\n\r\n// Configuration module\r\nPageRegistry.register(PAGE_KEYS.SETTINGS, lazy(() => import('@/pages/platform/administration/configuration/settings').then(m => ({ default: m.SettingsPage }))));\r\nPageRegistry.register(PAGE_KEYS.SETTINGS_GENERAL, lazy(() => import('@/pages/platform/administration/configuration/settings').then(m => ({ default: m.GeneralSettingsSection }))));\r\nPageRegistry.register(PAGE_KEYS.SETTINGS_FILE_UPLOAD, lazy(() => import('@/pages/platform/administration/configuration/settings').then(m => ({ default: m.FileUploadSettingsSection }))));\r\nPageRegistry.register(PAGE_KEYS.SETTINGS_LEGAL_FILE, lazy(() => import('@/pages/platform/administration/configuration/settings').then(m => ({ default: m.LegalFileSettingsSection }))));\r\nPageRegistry.register(PAGE_KEYS.APPSETTINGS, lazy(() => import('@/pages/platform/administration/configuration/appsettings').then(m => ({ default: m.AppSettingsPage }))));\r\nPageRegistry.register(PAGE_KEYS.APPSETTINGS_INFRASTRUCTURE, lazy(() => import('@/pages/platform/administration/configuration/appsettings').then(m => ({ default: m.InfrastructureSection }))));\r\nPageRegistry.register(PAGE_KEYS.APPSETTINGS_AUTHENTICATION, lazy(() => import('@/pages/platform/administration/configuration/appsettings').then(m => ({ default: m.AuthenticationSection }))));\r\nPageRegistry.register(PAGE_KEYS.APPSETTINGS_LOGGING, lazy(() => import('@/pages/platform/administration/configuration/appsettings').then(m => ({ default: m.LoggingSection }))));\r\nPageRegistry.register(PAGE_KEYS.APPSETTINGS_EMAIL, lazy(() => import('@/pages/platform/administration/configuration/appsettings').then(m => ({ default: m.EmailSection }))));\r\nPageRegistry.register(PAGE_KEYS.APPSETTINGS_ENTRA, lazy(() => import('@/pages/platform/administration/configuration/appsettings').then(m => ({ default: m.EntraSection }))));\r\nPageRegistry.register(PAGE_KEYS.LICENSE, lazy(() => import('@/pages/platform/administration/configuration/license').then(m => ({ default: m.LicenseManagementPage }))));\r\n\r\n// ============================================================================\r\n// API APPLICATION\r\n// ============================================================================\r\n\r\nPageRegistry.register(PAGE_KEYS.API_ACCOUNTS_LIST, lazy(() => import('@/pages/platform/api/external-apps').then(m => ({ default: m.ExternalAppsListPage }))));\r\nPageRegistry.register(PAGE_KEYS.API_ACCOUNTS_CREATE, lazy(() => import('@/pages/platform/api/external-apps').then(m => ({ default: m.ExternalAppCreatePage }))));\r\nPageRegistry.register(PAGE_KEYS.API_ACCOUNTS_DETAIL, lazy(() => import('@/pages/platform/api/external-apps').then(m => ({ default: m.ExternalAppDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.API_CATALOG, lazy(() => import('@/pages/platform/api/apis/ApiCatalogPage').then(m => ({ default: m.ApiCatalogPage }))));\r\nPageRegistry.register(PAGE_KEYS.API_CATALOG_DETAIL, lazy(() => import('@/pages/platform/api/apis/ApiCatalogDetailPage').then(m => ({ default: m.ApiCatalogDetailPage }))));\r\n\r\n// ============================================================================\r\n// SUPPORT APPLICATION\r\n// ============================================================================\r\n\r\nPageRegistry.register(PAGE_KEYS.SUPPORT_DASHBOARD, lazy(() => import('@/pages/platform/support/DashboardPage').then(m => ({ default: m.DashboardPage }))));\r\nPageRegistry.register(PAGE_KEYS.TICKETS_LIST, lazy(() => import('@/pages/platform/support/TicketsPage').then(m => ({ default: m.TicketsPage }))));\r\nPageRegistry.register(PAGE_KEYS.TICKETS_DETAIL, lazy(() => import('@/pages/platform/support/TicketDetailPage').then(m => ({ default: m.TicketDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.TICKETS_ESCALATION, lazy(() => import('@/pages/platform/support/EscalationConfigPage').then(m => ({ default: m.EscalationConfigPage }))));\r\nPageRegistry.register(PAGE_KEYS.TICKETS_NEW, lazy(() => import('@/pages/platform/support/CreateSupportTicketPage').then(m => ({ default: m.CreateSupportTicketPage }))));\r\nPageRegistry.register(PAGE_KEYS.MY_TICKETS_LIST, lazy(() => import('@/pages/platform/support/MyTicketsPage').then(m => ({ default: m.MyTicketsPage }))));\r\nPageRegistry.register(PAGE_KEYS.MY_TICKETS_DETAIL, lazy(() => import('@/pages/platform/support/UserTicketDetailPage').then(m => ({ default: m.UserTicketDetailPage }))));\r\nPageRegistry.register(PAGE_KEYS.MY_TICKETS_CREATE, lazy(() => import('@/pages/platform/support/UserCreateTicketPage').then(m => ({ default: m.UserCreateTicketPage }))));\r\nPageRegistry.register(PAGE_KEYS.SUPPORT_SLA, lazy(() => import('@/pages/platform/support/SlaConfigPage').then(m => ({ default: m.SlaConfigPage }))));\r\nPageRegistry.register(PAGE_KEYS.SUPPORT_TEMPLATES, lazy(() => import('@/pages/platform/support/TemplatesPage').then(m => ({ default: m.TemplatesPage }))));\r\nPageRegistry.register(PAGE_KEYS.SUPPORT_PERMISSIONS, lazy(() => import('@/pages/platform/support/SupportPermissionsPage').then(m => ({ default: m.SupportPermissionsPage }))));\r\nPageRegistry.register(PAGE_KEYS.SUPPORT_ASSIGNMENT, lazy(() => import('@/pages/platform/support/AssignmentRulesPage').then(m => ({ default: m.AssignmentRulesPage }))));\r\nPageRegistry.register(PAGE_KEYS.SUPPORT_ASSIGNMENT_SKILLS, lazy(() => import('@/pages/platform/support/AgentSkillsPage').then(m => ({ default: m.AgentSkillsPage }))));\r\nPageRegistry.register(PAGE_KEYS.SUPPORT_ASSIGNMENT_WORKLOAD, lazy(() => import('@/pages/platform/support/AgentWorkloadPage').then(m => ({ default: m.AgentWorkloadPage }))));\r\n\r\n// ============================================================================\r\n// MYSPACE APPLICATION\r\n// ============================================================================\r\n\r\nPageRegistry.register(PAGE_KEYS.MYSPACE_DASHBOARD, lazy(() => import('@/pages/personal/myspace/UserDashboardPage').then(m => ({ default: m.UserDashboardPage }))));\r\nPageRegistry.register(PAGE_KEYS.USER_PROFILE, lazy(() => import('@/pages/personal/myspace/ProfilePage').then(m => ({ default: m.ProfilePage }))));\r\nPageRegistry.register(PAGE_KEYS.USER_PREFERENCES, lazy(() => import('@/pages/personal/myspace/PreferencesPage').then(m => ({ default: m.PreferencesPage }))));\r\nPageRegistry.register(PAGE_KEYS.MYSPACE_TENANTS, lazy(() => import('@/pages/personal/myspace/MyTenantsPage').then(m => ({ default: m.MyTenantsPage }))));\r\nPageRegistry.register(PAGE_KEYS.MYSPACE_ACCESS_REQUESTS, lazy(() => import('@/pages/personal/myspace/MyAccessRequestsPage')));\r\nPageRegistry.register(PAGE_KEYS.MYSPACE_PORTAL, lazy(() => import('@/pages/personal/myspace/PortalDashboardPage').then(m => ({ default: m.PortalDashboardPage }))));\r\n\r\n// ============================================================================\r\n// SYSTEM / OPEN APPS (not from DB navigation)\r\n// ============================================================================\r\n\r\nPageRegistry.register(PAGE_KEYS.NOTIFICATIONS, lazy(() => import('@/pages/notifications/NotificationsPage').then(m => ({ default: m.NotificationsPage }))));\r\nPageRegistry.register(PAGE_KEYS.APPLICATIONS_HUB, lazy(() => import('@/pages/ApplicationsPage').then(m => ({ default: m.ApplicationsPage }))));\r\n","import { createContext, useContext, useMemo, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { ThemeProvider } from '@/contexts/ThemeContext';\r\nimport { FeatureConfigProvider } from '@/contexts/FeatureConfigContext';\r\nimport { AuthProvider } from '@/contexts/AuthContext';\r\nimport { LicenseProvider } from '@/contexts/LicenseContext';\r\nimport { FavoritesProvider } from '@/contexts/FavoritesContext';\r\nimport { SidebarProvider } from '@/contexts/SidebarContext';\r\nimport { TenantProvider } from '@/contexts/TenantContext';\r\nimport { NavigationProvider } from '@/contexts/NavigationContext';\r\nimport { SignalRProvider } from '@/contexts/SignalRContext';\r\nimport { SessionProvider } from '@/components/session/SessionProvider';\r\nimport { ThemeSync } from '@/components/ThemeSync';\r\nimport { GlobalErrorBoundary } from '@/components/errors/GlobalErrorBoundary';\r\nimport { ExtensionProvider } from '@/extensions/ExtensionContext';\r\nimport type { SmartStackConfig } from '@/router/types';\r\nimport '@/services/logging/logService';\r\n\r\n// Auto-register built-in SmartStack pages so DynamicRouter can resolve them.\r\n// Client pages registered AFTER this import will override built-in ones.\r\nimport '@/extensions/componentRegistry.generated';\r\n\r\n/**\r\n * SmartStack context value\r\n */\r\ninterface SmartStackContextValue {\r\n config: SmartStackConfig;\r\n}\r\n\r\nconst SmartStackContext = createContext<SmartStackContextValue | null>(null);\r\n\r\n/**\r\n * Hook to access SmartStack configuration\r\n */\r\nexport function useSmartStack(): SmartStackContextValue {\r\n const context = useContext(SmartStackContext);\r\n if (!context) {\r\n throw new Error('useSmartStack must be used within a SmartStackProvider');\r\n }\r\n return context;\r\n}\r\n\r\n/**\r\n * Props for SmartStackProvider\r\n */\r\ninterface SmartStackProviderProps {\r\n /**\r\n * SmartStack configuration\r\n */\r\n readonly config: SmartStackConfig;\r\n\r\n /**\r\n * Child components\r\n */\r\n readonly children: ReactNode;\r\n}\r\n\r\n/**\r\n * SmartStackProvider - Wraps your application with all SmartStack context providers\r\n *\r\n * @example\r\n * ```tsx\r\n * import { SmartStackProvider, DynamicRouter } from '@atlashub/smartstack';\r\n * import { BrowserRouter } from 'react-router-dom';\r\n *\r\n * // BrowserRouter MUST wrap SmartStackProvider (NavigationProvider needs router context)\r\n * function App() {\r\n * return (\r\n * <BrowserRouter>\r\n * <SmartStackProvider config={{ apiUrl: 'http://localhost:5142' }}>\r\n * <DynamicRouter />\r\n * </SmartStackProvider>\r\n * </BrowserRouter>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function SmartStackProvider({ config, children }: SmartStackProviderProps): ReactElement {\r\n // Set API URL in window for services that need it\r\n if (typeof window !== 'undefined') {\r\n (window as unknown as Record<string, unknown>).__SMARTSTACK_CONFIG__ = config;\r\n }\r\n\r\n const contextValue = useMemo(() => ({ config }), [config]);\r\n\r\n return (\r\n <SmartStackContext.Provider value={contextValue}>\r\n <GlobalErrorBoundary>\r\n <ExtensionProvider config={config.extensions}>\r\n <ThemeProvider>\r\n <FeatureConfigProvider>\r\n <AuthProvider>\r\n <TenantProvider>\r\n <LicenseProvider>\r\n <FavoritesProvider>\r\n <SidebarProvider>\r\n <SessionProvider>\r\n <ThemeSync>\r\n <NavigationProvider>\r\n <SignalRProvider>\r\n {children}\r\n </SignalRProvider>\r\n </NavigationProvider>\r\n </ThemeSync>\r\n </SessionProvider>\r\n </SidebarProvider>\r\n </FavoritesProvider>\r\n </LicenseProvider>\r\n </TenantProvider>\r\n </AuthProvider>\r\n </FeatureConfigProvider>\r\n </ThemeProvider>\r\n </ExtensionProvider>\r\n </GlobalErrorBoundary>\r\n </SmartStackContext.Provider>\r\n );\r\n}\r\n","/* eslint-disable react-refresh/only-export-components */\r\nimport type { ReactElement } from 'react';\r\nimport { createContext, useContext, useState, useCallback, useEffect, useRef, type ReactNode } from 'react';\r\nimport { useLocation } from 'react-router-dom';\r\n\r\nconst DOC_PANEL_STORAGE_KEY = 'smartstack-doc-panel-size';\r\n\r\ninterface DocPanelContextType {\r\n isOpen: boolean;\r\n panelSize: number;\r\n docUrl: string | null;\r\n openDocPanel: (url?: string) => void;\r\n closeDocPanel: () => void;\r\n toggleDocPanel: () => void;\r\n setPanelSize: (size: number) => void;\r\n}\r\n\r\nconst DocPanelContext = createContext<DocPanelContextType | undefined>(undefined);\r\n\r\nfunction loadPanelSize(): number {\r\n if (typeof window === 'undefined') return 33;\r\n try {\r\n const saved = localStorage.getItem(DOC_PANEL_STORAGE_KEY);\r\n if (saved) {\r\n const size = parseFloat(saved);\r\n if (size >= 20 && size <= 60) return size;\r\n }\r\n } catch {\r\n // Ignore\r\n }\r\n return 33;\r\n}\r\n\r\n// Mapping: route path → documentation URL\r\n// Format: '{application}/{module}' or '{application}/{module}/{section}'\r\n// Keys are prefixed with the application code to avoid ambiguity\r\nconst docMapping: Record<string, string> = {\r\n // Administration > Users\r\n 'administration/users': '/docs/business/administration/users',\r\n 'administration/users/dashboard': '/docs/business/administration/users',\r\n 'administration/users/list': '/docs/business/administration/users',\r\n 'administration/users/create': '/docs/business/administration/users',\r\n 'administration/users/groups': '/docs/business/administration/users/groups',\r\n 'administration/users/references': '/docs/business/administration/users/references',\r\n\r\n // Administration > Permissions\r\n 'administration/permissions': '/docs/business/administration/permissions',\r\n 'administration/permissions/roles': '/docs/business/administration/permissions',\r\n 'administration/permissions/permissions': '/docs/business/administration/permissions',\r\n 'administration/permissions/assignments': '/docs/business/administration/permissions/assignments',\r\n\r\n // Administration > Applications\r\n 'administration/applications': '/docs/business/administration/applications',\r\n 'administration/applications/list': '/docs/business/administration/applications',\r\n 'administration/applications/grid': '/docs/business/administration/applications',\r\n 'administration/applications/dashboard': '/docs/business/administration/applications',\r\n\r\n // Administration > Tenants\r\n 'administration/tenants': '/docs/business/administration/tenants',\r\n 'administration/tenants/dashboard': '/docs/business/administration/tenants',\r\n 'administration/tenants/create': '/docs/business/administration/tenants',\r\n 'administration/tenants/list': '/docs/business/administration/tenants/business',\r\n 'administration/tenants/list/enterprise': '/docs/business/administration/tenants/business',\r\n 'administration/tenants/list/personal': '/docs/business/administration/tenants/personal',\r\n 'administration/tenants/list/other': '/docs/business/administration/tenants/other',\r\n 'administration/tenants/template': '/docs/business/administration/tenants/template',\r\n 'administration/tenants/business': '/docs/business/administration/tenants/business',\r\n 'administration/tenants/personal': '/docs/business/administration/tenants',\r\n 'administration/tenants/organisations': '/docs/business/administration/tenants/organisations',\r\n 'administration/tenants/organisations/create': '/docs/business/administration/tenants/organisations',\r\n 'administration/tenants/access-requests': '/docs/business/administration/tenants/access-requests',\r\n 'administration/tenants/applications': '/docs/business/administration/tenants/applications',\r\n\r\n // Administration > Workflows\r\n 'administration/workflows': '/docs/business/administration/workflows',\r\n 'administration/workflows/list': '/docs/business/administration/workflows',\r\n 'administration/workflows/create': '/docs/business/administration/workflows',\r\n 'administration/workflows/triggers': '/docs/business/administration/workflows',\r\n 'administration/workflows/history': '/docs/business/administration/workflows',\r\n 'administration/workflows/email-templates': '/docs/business/administration/workflows',\r\n 'administration/workflows/email-templates/email': '/docs/business/administration/workflows',\r\n 'administration/workflows/email-templates/sms': '/docs/business/administration/workflows',\r\n 'administration/workflows/email-templates/create': '/docs/business/administration/workflows',\r\n\r\n // Administration > AI\r\n 'administration/ai': '/docs/business/administration/ai',\r\n 'administration/ai/dashboard': '/docs/business/administration/ai',\r\n 'administration/ai/settings': '/docs/business/administration/ai',\r\n 'administration/ai/providers': '/docs/business/administration/ai',\r\n 'administration/ai/models': '/docs/business/administration/ai',\r\n 'administration/ai/prompts': '/docs/business/administration/ai',\r\n 'administration/ai/blocks': '/docs/business/administration/ai',\r\n\r\n // Administration > Microsoft Entra ID\r\n 'administration/entra': '/docs/business/administration/entra',\r\n 'administration/entra/dashboard': '/docs/business/administration/entra',\r\n 'administration/entra/groups': '/docs/business/administration/entra',\r\n 'administration/entra/users': '/docs/business/administration/entra',\r\n 'administration/entra/conflicts': '/docs/business/administration/entra',\r\n 'administration/entra/settings': '/docs/business/administration/entra',\r\n\r\n // Administration > Configuration\r\n 'administration/configuration': '/docs/business/administration/configuration',\r\n 'administration/configuration/settings': '/docs/business/administration/configuration',\r\n 'administration/configuration/appsettings': '/docs/business/administration/configuration',\r\n 'administration/configuration/license': '/docs/business/administration/configuration/license',\r\n\r\n // Support - Tickets module (dashboard + list + escalation)\r\n 'support/tickets': '/system/docs/user/support-dashboard',\r\n 'support/tickets/list': '/docs/business/platform/support/tickets',\r\n 'support/tickets/escalation': '/system/docs/user/support-escalation',\r\n 'support/my-tickets': '/docs/business/platform/support/tickets',\r\n 'support/my-tickets/list': '/docs/business/platform/support/tickets',\r\n 'support/my-tickets/create': '/docs/business/platform/support/tickets',\r\n // Support - Admin (Paramètres) module\r\n 'support/admin/sla': '/system/docs/user/support-sla',\r\n 'support/admin/templates': '/system/docs/user/support-templates',\r\n\r\n // MySpace\r\n 'myspace/profile': '/system/docs/user/modules',\r\n 'myspace/preferences': '/system/docs/user/modules',\r\n 'myspace/tenants': '/system/docs/user/modules',\r\n 'myspace/portal': '/system/docs/user/modules',\r\n};\r\n\r\n// Application-level fallbacks\r\nconst appDocMapping: Record<string, string> = {\r\n 'administration': '/docs/business/administration/users',\r\n 'support': '/system/docs/user/support-dashboard',\r\n 'myspace': '/system/docs/user/modules',\r\n};\r\n\r\nfunction getDocUrlForPath(pathname: string): string {\r\n // Standard: /administration/workflows/email-templates → ['administration', 'workflows', 'email-templates']\r\n // Tenant: /t/my-company/administration/workflows → ['t', 'my-company', 'administration', 'workflows']\r\n const segments = pathname.split('/').filter(Boolean);\r\n\r\n // Detect tenant-prefixed routes: /t/:slug/...\r\n const isTenantRoute = segments[0] === 't' && segments.length >= 3;\r\n const offset = isTenantRoute ? 2 : 0;\r\n\r\n // appSegments: [application, module, section, ...]\r\n const appSegments = segments.slice(offset);\r\n const appCode = appSegments[0];\r\n const moduleParts = appSegments.slice(1);\r\n\r\n // Try from most specific to least specific: app/module/section → app/module\r\n if (moduleParts.length > 0) {\r\n for (let i = moduleParts.length; i > 0; i--) {\r\n const key = `${appCode}/${moduleParts.slice(0, i).join('/')}`;\r\n if (docMapping[key]) {\r\n return docMapping[key];\r\n }\r\n }\r\n }\r\n\r\n // Fall back to application-level documentation\r\n if (appCode && appDocMapping[appCode]) {\r\n return appDocMapping[appCode];\r\n }\r\n\r\n return '/system/docs/user';\r\n}\r\n\r\nexport function DocPanelProvider({ children }: { children: ReactNode }): ReactElement {\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [panelSize, setPanelSizeState] = useState(loadPanelSize);\r\n const [docUrl, setDocUrl] = useState<string | null>(null);\r\n const location = useLocation();\r\n const prevPathnameRef = useRef(location.pathname);\r\n\r\n useEffect(() => {\r\n if (prevPathnameRef.current !== location.pathname) {\r\n prevPathnameRef.current = location.pathname;\r\n // Close doc panel on route change - this is a legitimate use case\r\n // eslint-disable-next-line react-hooks/set-state-in-effect\r\n setIsOpen(false);\r\n }\r\n }, [location.pathname]);\r\n\r\n useEffect(() => {\r\n if (panelSize >= 20 && panelSize <= 60) {\r\n localStorage.setItem(DOC_PANEL_STORAGE_KEY, String(panelSize));\r\n }\r\n }, [panelSize]);\r\n\r\n const openDocPanel = useCallback((url?: string) => {\r\n const targetUrl = url || getDocUrlForPath(location.pathname);\r\n setDocUrl(targetUrl);\r\n setIsOpen(true);\r\n }, [location.pathname]);\r\n\r\n const closeDocPanel = useCallback(() => {\r\n setIsOpen(false);\r\n }, []);\r\n\r\n const toggleDocPanel = useCallback(() => {\r\n if (isOpen) {\r\n setIsOpen(false);\r\n } else {\r\n const targetUrl = getDocUrlForPath(location.pathname);\r\n setDocUrl(targetUrl);\r\n setIsOpen(true);\r\n }\r\n }, [isOpen, location.pathname]);\r\n\r\n const setPanelSize = useCallback((size: number) => {\r\n if (size >= 20 && size <= 60) {\r\n setPanelSizeState(size);\r\n }\r\n }, []);\r\n\r\n return (\r\n <DocPanelContext.Provider value={{\r\n isOpen,\r\n panelSize,\r\n docUrl,\r\n openDocPanel,\r\n closeDocPanel,\r\n toggleDocPanel,\r\n setPanelSize,\r\n }}>\r\n {children}\r\n </DocPanelContext.Provider>\r\n );\r\n}\r\n\r\nexport function useDocPanel(): DocPanelContextType {\r\n const context = useContext(DocPanelContext);\r\n if (context === undefined) {\r\n throw new Error('useDocPanel must be used within a DocPanelProvider');\r\n }\r\n return context;\r\n}\r\n","export interface BrowserInfo {\r\n browser: string;\r\n browserVersion: string;\r\n os: string;\r\n osVersion: string;\r\n language: string;\r\n screenResolution: string;\r\n deviceType: 'Desktop' | 'Mobile' | 'Tablet';\r\n}\r\n\r\nclass BrowserInfoService {\r\n /**\r\n * Capture browser and system information (without URL, without console errors)\r\n */\r\n captureBrowserInfo(): BrowserInfo {\r\n const userAgent = navigator.userAgent;\r\n\r\n return {\r\n browser: this.getBrowserName(userAgent),\r\n browserVersion: this.getBrowserVersion(userAgent),\r\n os: this.getOSName(userAgent),\r\n osVersion: this.getOSVersion(userAgent),\r\n language: navigator.language,\r\n screenResolution: `${window.screen.width}x${window.screen.height}`,\r\n deviceType: this.getDeviceType(userAgent),\r\n };\r\n }\r\n\r\n private getBrowserName(userAgent: string): string {\r\n if (userAgent.includes('Firefox')) return 'Firefox';\r\n if (userAgent.includes('Edg/')) return 'Microsoft Edge';\r\n if (userAgent.includes('Chrome') && !userAgent.includes('Edg/')) return 'Google Chrome';\r\n if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) return 'Safari';\r\n if (userAgent.includes('Opera') || userAgent.includes('OPR/')) return 'Opera';\r\n if (userAgent.includes('MSIE') || userAgent.includes('Trident/')) return 'Internet Explorer';\r\n return 'Unknown Browser';\r\n }\r\n\r\n private getBrowserVersion(userAgent: string): string {\r\n let match: RegExpExecArray | null = null;\r\n\r\n if (userAgent.includes('Firefox')) {\r\n match = /Firefox\\/(\\d+(?:\\.\\d+)*)/.exec(userAgent);\r\n } else if (userAgent.includes('Edg/')) {\r\n match = /Edg\\/(\\d+(?:\\.\\d+)*)/.exec(userAgent);\r\n } else if (userAgent.includes('Chrome') && !userAgent.includes('Edg/')) {\r\n match = /Chrome\\/(\\d+(?:\\.\\d+)*)/.exec(userAgent);\r\n } else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {\r\n match = /Version\\/(\\d+(?:\\.\\d+)*)/.exec(userAgent);\r\n } else if (userAgent.includes('OPR/')) {\r\n match = /OPR\\/(\\d+(?:\\.\\d+)*)/.exec(userAgent);\r\n }\r\n\r\n return match ? match[1] : 'Unknown';\r\n }\r\n\r\n private getOSName(userAgent: string): string {\r\n if (userAgent.includes('Windows NT 10')) return 'Windows 10/11';\r\n if (userAgent.includes('Windows NT 6.3')) return 'Windows 8.1';\r\n if (userAgent.includes('Windows NT 6.2')) return 'Windows 8';\r\n if (userAgent.includes('Windows NT 6.1')) return 'Windows 7';\r\n if (userAgent.includes('Windows')) return 'Windows';\r\n if (userAgent.includes('Mac OS X')) return 'macOS';\r\n if (userAgent.includes('Linux') && userAgent.includes('Android')) return 'Android';\r\n if (userAgent.includes('Linux')) return 'Linux';\r\n if (userAgent.includes('iPhone') || userAgent.includes('iPad')) return 'iOS';\r\n if (userAgent.includes('CrOS')) return 'Chrome OS';\r\n return 'Unknown OS';\r\n }\r\n\r\n private getWindowsVersion(ntVersion: string): string {\r\n if (ntVersion === '10.0') return '10/11';\r\n if (ntVersion === '6.3') return '8.1';\r\n if (ntVersion === '6.2') return '8';\r\n if (ntVersion === '6.1') return '7';\r\n return ntVersion;\r\n }\r\n\r\n private getOSVersion(userAgent: string): string {\r\n if (userAgent.includes('Windows NT')) {\r\n const match = /Windows NT (\\d+\\.\\d+)/.exec(userAgent);\r\n if (match) return this.getWindowsVersion(match[1]);\r\n }\r\n if (userAgent.includes('Mac OS X')) {\r\n const match = /Mac OS X (\\d+[._]\\d+(?:[._]\\d+)?)/.exec(userAgent);\r\n if (match) return match[1].replace(/_/g, '.');\r\n }\r\n if (userAgent.includes('Android')) {\r\n const match = /Android (\\d+(?:\\.\\d+)*)/.exec(userAgent);\r\n if (match) return match[1];\r\n }\r\n if (userAgent.includes('iPhone OS') || userAgent.includes('iPad')) {\r\n const match = /(?:iPhone|iPad|CPU) OS (\\d+[._]\\d+(?:[._]\\d+)?)/.exec(userAgent);\r\n if (match) return match[1].replace(/_/g, '.');\r\n }\r\n return 'Unknown';\r\n }\r\n\r\n private getDeviceType(userAgent: string): 'Desktop' | 'Mobile' | 'Tablet' {\r\n if (/Tablet|iPad/i.test(userAgent)) return 'Tablet';\r\n if (/Mobile|iPhone|Android.*Mobile/i.test(userAgent)) return 'Mobile';\r\n return 'Desktop';\r\n }\r\n\r\n /**\r\n * Format browser info for display in ticket description\r\n */\r\n formatForDescription(info: BrowserInfo): string {\r\n return [\r\n `Browser: ${info.browser} ${info.browserVersion}`,\r\n `OS: ${info.os} ${info.osVersion}`,\r\n `Device: ${info.deviceType}`,\r\n `Screen: ${info.screenResolution}`,\r\n `Language: ${info.language}`,\r\n ].join('\\n');\r\n }\r\n}\r\n\r\nexport const browserInfoService = new BrowserInfoService();\r\n","import { useCallback } from 'react';\r\nimport { browserInfoService } from '@/services/support/browserInfoService';\r\nimport { logService } from '@/services/logging/logService';\r\nimport type { ClientContextDto } from '@/services/api/ticketApi';\r\n\r\n/**\r\n * Hook to capture client context for ticket creation.\r\n * Captures browser info, OS info, device type, and recent errors.\r\n */\r\nexport function useClientContext(): {\n captureContext: () => ClientContextDto;\n} {\r\n const captureContext = useCallback((): ClientContextDto => {\r\n const browserInfo = browserInfoService.captureBrowserInfo();\r\n const recentErrors = logService.getRecentErrors().slice(0, 5);\r\n\r\n return {\r\n sourceUrl: window.location.href,\r\n browser: {\r\n name: browserInfo.browser,\r\n version: browserInfo.browserVersion,\r\n language: browserInfo.language,\r\n },\r\n os: {\r\n name: browserInfo.os,\r\n version: browserInfo.osVersion,\r\n },\r\n device: {\r\n type: browserInfo.deviceType,\r\n screenResolution: browserInfo.screenResolution,\r\n },\r\n recentErrors: recentErrors.map((error) => ({\r\n message: error.message,\r\n stack: error.stack,\r\n component: error.component,\r\n timestamp: error.timestamp.toISOString(),\r\n })),\r\n };\r\n }, []);\r\n\r\n return { captureContext };\r\n}\r\n","import { useState, useCallback } from 'react';\r\n\r\n/**\r\n * Hook to manage collapse/expand state for hierarchical lists.\r\n * Used by permission matrix components to track collapsed contexts and applications.\r\n */\r\nexport function useCollapsibleState(): {\r\n collapsedContexts: Set<string>;\r\n collapsedApps: Set<string>;\r\n toggleContext: (contextKey: string) => void;\r\n toggleApp: (appKey: string) => void;\r\n expandAll: () => void;\r\n collapseAll: (contexts: string[], apps: string[]) => void;\r\n isContextCollapsed: (contextKey: string) => boolean;\r\n isAppCollapsed: (appKey: string) => boolean;\r\n} {\r\n const [collapsedContexts, setCollapsedContexts] = useState<Set<string>>(new Set());\r\n const [collapsedApps, setCollapsedApps] = useState<Set<string>>(new Set());\r\n\r\n const toggleContext = useCallback((contextKey: string) => {\r\n setCollapsedContexts(prev => {\r\n const next = new Set(prev);\r\n if (next.has(contextKey)) {\r\n next.delete(contextKey);\r\n } else {\r\n next.add(contextKey);\r\n }\r\n return next;\r\n });\r\n }, []);\r\n\r\n const toggleApp = useCallback((appKey: string) => {\r\n setCollapsedApps(prev => {\r\n const next = new Set(prev);\r\n if (next.has(appKey)) {\r\n next.delete(appKey);\r\n } else {\r\n next.add(appKey);\r\n }\r\n return next;\r\n });\r\n }, []);\r\n\r\n const expandAll = useCallback(() => {\r\n setCollapsedContexts(new Set());\r\n setCollapsedApps(new Set());\r\n }, []);\r\n\r\n const collapseAll = useCallback((contexts: string[], apps: string[]) => {\r\n setCollapsedContexts(new Set(contexts));\r\n setCollapsedApps(new Set(apps));\r\n }, []);\r\n\r\n const isContextCollapsed = useCallback((contextKey: string) => {\r\n return collapsedContexts.has(contextKey);\r\n }, [collapsedContexts]);\r\n\r\n const isAppCollapsed = useCallback((appKey: string) => {\r\n return collapsedApps.has(appKey);\r\n }, [collapsedApps]);\r\n\r\n return {\r\n collapsedContexts,\r\n collapsedApps,\r\n toggleContext,\r\n toggleApp,\r\n expandAll,\r\n collapseAll,\r\n isContextCollapsed,\r\n isAppCollapsed,\r\n };\r\n}\r\n","import { useEffect, useRef } from 'react';\r\nimport { useLocation } from 'react-router-dom';\r\nimport { api } from '@/services/api/apiClient';\r\n\r\n// Technical API endpoint (not a navigation module)\r\nconst TRACKING_API = '/api/myspace/application-tracking';\r\nconst EMPTY_GUID = '00000000-0000-0000-0000-000000000000';\r\n\r\nconst isValidAccessId = (accessId: string | null): accessId is string =>\r\n !!accessId && accessId !== EMPTY_GUID;\r\n\r\n/**\r\n * Strip the /t/{slug}/ tenant prefix from a pathname.\r\n * Example: /t/techstart/user → /user\r\n */\r\nconst stripTenantPrefix = (pathname: string): string => {\r\n const match = /^\\/t\\/[^/]+(\\/.*)$/.exec(pathname);\r\n return match ? match[1] : pathname;\r\n};\r\n\r\nconst isApplicationRoute = (pathname: string): boolean => {\r\n const cleanPath = stripTenantPrefix(pathname);\r\n return cleanPath.startsWith('/administration/') || cleanPath.startsWith('/support/') || cleanPath.startsWith('/myspace/');\r\n};\r\n\r\nconst shouldLogError = (error: unknown, ignoredStatuses: number[]): boolean => {\r\n if (!error || typeof error !== 'object' || !('status' in error)) return true;\r\n const status = (error as { status?: number }).status;\r\n return !ignoredStatuses.includes(status ?? -1);\r\n};\r\n\r\nconst endPreviousTracking = async (accessId: string, startTime: Date): Promise<void> => {\r\n const endedAt = new Date();\r\n const durationSeconds = Math.floor((endedAt.getTime() - startTime.getTime()) / 1000);\r\n\r\n try {\r\n await api.post(`${TRACKING_API}/end`, {\r\n accessId,\r\n endedAt: endedAt.toISOString(),\r\n durationSeconds\r\n });\r\n } catch (error) {\r\n if (shouldLogError(error, [401, 408])) {\r\n console.error('Failed to end page tracking:', error);\r\n }\r\n }\r\n};\r\n\r\nconst startNewTracking = async (route: string): Promise<string | null> => {\r\n const accessedAt = new Date();\r\n\r\n try {\r\n const response = await api.post<{ accessId: string }>(`${TRACKING_API}/start`, {\r\n route,\r\n accessedAt: accessedAt.toISOString()\r\n });\r\n return response.accessId;\r\n } catch (error) {\r\n if (shouldLogError(error, [401])) {\r\n console.error('Failed to track page access:', error);\r\n }\r\n return null;\r\n }\r\n};\r\n\r\nconst endTrackingOnUnmount = (accessId: string, startTime: Date): void => {\r\n const token = localStorage.getItem('token');\r\n if (!token) return;\r\n\r\n const endedAt = new Date();\r\n const durationSeconds = Math.floor((endedAt.getTime() - startTime.getTime()) / 1000);\r\n\r\n fetch(`${TRACKING_API}/end`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Authorization': `Bearer ${token}`\r\n },\r\n body: JSON.stringify({\r\n accessId,\r\n endedAt: endedAt.toISOString(),\r\n durationSeconds\r\n }),\r\n keepalive: true\r\n }).catch(() => {\r\n // Silently ignore errors on unmount - tracking is best-effort\r\n });\r\n};\r\n\r\nexport function usePageTracking(): void {\r\n const location = useLocation();\r\n const accessStartRef = useRef<Date>(new Date());\r\n const currentAccessIdRef = useRef<string | null>(null);\r\n const isTrackingRef = useRef<boolean>(false);\r\n\r\n useEffect(() => {\r\n if (isTrackingRef.current) {\r\n return;\r\n }\r\n\r\n const trackPageAccess = async () => {\r\n const route = stripTenantPrefix(location.pathname);\r\n\r\n if (!isApplicationRoute(route)) {\r\n return;\r\n }\r\n\r\n isTrackingRef.current = true;\r\n\r\n if (isValidAccessId(currentAccessIdRef.current)) {\r\n await endPreviousTracking(currentAccessIdRef.current, accessStartRef.current);\r\n }\r\n\r\n accessStartRef.current = new Date();\r\n currentAccessIdRef.current = await startNewTracking(route);\r\n isTrackingRef.current = false;\r\n };\r\n\r\n trackPageAccess();\r\n\r\n return () => {\r\n if (isValidAccessId(currentAccessIdRef.current)) {\r\n endTrackingOnUnmount(currentAccessIdRef.current, accessStartRef.current);\r\n }\r\n };\r\n }, [location.pathname]);\r\n}\r\n","import { useState, useEffect, useMemo, useCallback, useRef } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { adminApi } from '@/services/api/adminApi';\r\nimport type { PermissionTreeDto, PermissionListDto, MatrixRoleDto, RoleCategory, PermissionApplicationNode, PermissionModuleNode, PermissionSectionNode, PermissionResourceNode } from '@/services/api/adminApi';\r\nimport { matchesWildcard } from '@/utils/permissions';\r\n\r\nexport interface ResourceNode {\r\n id: string;\r\n path: string;\r\n label: string;\r\n applicationLabel: string;\r\n moduleLabel: string;\r\n parentModuleLabel?: string; // For sections/resources: the parent module name\r\n parentModuleId?: string; // For sections/resources: the parent module ID\r\n parentSectionLabel?: string; // For resources: the parent section name\r\n parentSectionId?: string; // For resources: the parent section ID\r\n level: 'module' | 'section' | 'resource';\r\n permissions: ResourcePermission[];\r\n totalPermissions: number;\r\n childSections?: ResourceNode[]; // For modules: child sections\r\n childResources?: ResourceNode[]; // For sections: child resources\r\n}\r\n\r\nexport interface ResourcePermission {\r\n id: string;\r\n action: string;\r\n path: string;\r\n}\r\n\r\nexport interface PermissionInheritanceInfo {\r\n isDirect: boolean;\r\n isInherited: boolean;\r\n inheritedFromPath?: string;\r\n inheritedFromId?: string;\r\n}\r\n\r\nexport interface InheritanceMap {\r\n [roleId: string]: Map<string, PermissionInheritanceInfo>;\r\n}\r\n\r\nexport interface RolePermissions {\r\n [roleId: string]: Set<string>;\r\n}\r\n\r\nexport interface MatrixFilters {\r\n selectedRoleIds: string[];\r\n resourceSearch: string;\r\n applicationFilter: string;\r\n moduleFilter: string;\r\n categoryFilter: string;\r\n}\r\n\r\nexport interface UsePermissionMatrixResult {\r\n loading: boolean;\r\n error: string | null;\r\n saveError: string | null;\r\n saving: boolean;\r\n saveSuccess: boolean;\r\n roles: MatrixRoleDto[];\r\n resources: ResourceNode[];\r\n permissions: Map<string, PermissionListDto>;\r\n assignments: RolePermissions;\r\n inheritanceMap: InheritanceMap;\r\n filters: MatrixFilters;\r\n filteredRoles: MatrixRoleDto[];\r\n filteredResources: ResourceNode[];\r\n hasChanges: boolean;\r\n canEditSystemRoles: boolean;\r\n applications: { app: string; label: string }[];\r\n modules: { app: string; module: string; label: string }[];\r\n categories: RoleCategory[];\r\n togglePermission: (roleId: string, permissionId: string) => void;\r\n toggleAllForRole: (roleId: string, grant: boolean) => void;\r\n toggleAllForResource: (resourceNode: ResourceNode, grant: boolean) => void;\r\n updateFilters: (filters: Partial<MatrixFilters>) => void;\r\n reloadData: () => Promise<void>;\r\n resetChanges: () => void;\r\n saveChanges: () => Promise<void>;\r\n clearSaveError: () => void;\r\n getPermissionInheritance: (roleId: string, permissionId: string) => PermissionInheritanceInfo;\r\n}\r\n\r\nexport interface UsePermissionMatrixOptions {\r\n canEditSystemRoles?: boolean;\r\n}\r\n\r\nexport function usePermissionMatrix(options: UsePermissionMatrixOptions = {}): UsePermissionMatrixResult {\r\n const { canEditSystemRoles = false } = options;\r\n const { t, i18n } = useTranslation('admin');\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n const [saveError, setSaveError] = useState<string | null>(null);\r\n const [saving, setSaving] = useState(false);\r\n const [saveSuccess, setSaveSuccess] = useState(false);\r\n const [roles, setRoles] = useState<MatrixRoleDto[]>([]);\r\n const [resources, setResources] = useState<ResourceNode[]>([]);\r\n const [permissions, setPermissions] = useState<Map<string, PermissionListDto>>(new Map());\r\n const [assignments, setAssignments] = useState<RolePermissions>({});\r\n const [originalAssignments, setOriginalAssignments] = useState<RolePermissions>({});\r\n const [inheritanceMap, setInheritanceMap] = useState<InheritanceMap>({});\r\n const [filters, setFilters] = useState<MatrixFilters>({\r\n selectedRoleIds: [],\r\n resourceSearch: '',\r\n applicationFilter: 'all',\r\n moduleFilter: 'all',\r\n categoryFilter: 'all',\r\n });\r\n\r\n const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const changesRef = useRef<Map<string, Set<string>>>(new Map());\r\n\r\n // matchesWildcard is now imported from @/utils/permissions\r\n\r\n const calculateInheritanceForRole = useCallback((\r\n rolePermissions: Set<string>,\r\n permissionsMap: Map<string, PermissionListDto>,\r\n targetPermissionId: string\r\n ): PermissionInheritanceInfo => {\r\n const isDirect = rolePermissions.has(targetPermissionId);\r\n const targetPerm = permissionsMap.get(targetPermissionId);\r\n if (!targetPerm) {\r\n return { isDirect, isInherited: false };\r\n }\r\n\r\n const targetPath = targetPerm.path.toLowerCase();\r\n let inheritedFromPath: string | undefined;\r\n let inheritedFromId: string | undefined;\r\n let shortestWildcardLength = Infinity;\r\n\r\n // Check all permissions the role has for wildcards that cover this target\r\n for (const permId of Array.from(rolePermissions)) {\r\n const perm = permissionsMap.get(permId);\r\n if (!perm || permId === targetPermissionId) continue;\r\n\r\n // Check if this permission is a wildcard (either by flag or by path ending with .*)\r\n const permPath = perm.path.toLowerCase();\r\n const isWildcard = perm.isWildcard || permPath.endsWith('.*');\r\n\r\n if (!isWildcard) continue;\r\n\r\n if (matchesWildcard(permPath, targetPath)) {\r\n const wildcardPathLength = permPath.split('.').length;\r\n if (wildcardPathLength < shortestWildcardLength) {\r\n shortestWildcardLength = wildcardPathLength;\r\n inheritedFromPath = perm.path;\r\n inheritedFromId = perm.id;\r\n }\r\n }\r\n }\r\n\r\n const isInherited = inheritedFromPath !== undefined;\r\n return {\r\n isDirect,\r\n isInherited,\r\n inheritedFromPath,\r\n inheritedFromId,\r\n };\r\n }, []);\r\n\r\n const calculateInheritanceMap = useCallback((\r\n assignmentsMap: RolePermissions,\r\n permissionsMap: Map<string, PermissionListDto>\r\n ): InheritanceMap => {\r\n const inheritanceMap: InheritanceMap = {};\r\n\r\n Object.entries(assignmentsMap).forEach(([roleId, rolePermissions]) => {\r\n const roleInheritanceMap = new Map<string, PermissionInheritanceInfo>();\r\n\r\n permissionsMap.forEach((_perm, permId) => {\r\n const inheritanceInfo = calculateInheritanceForRole(\r\n rolePermissions,\r\n permissionsMap,\r\n permId\r\n );\r\n roleInheritanceMap.set(permId, inheritanceInfo);\r\n });\r\n\r\n inheritanceMap[roleId] = roleInheritanceMap;\r\n });\r\n\r\n return inheritanceMap;\r\n }, [calculateInheritanceForRole]);\r\n\r\n const buildResourceNodes = (\r\n section: PermissionSectionNode,\r\n module: PermissionModuleNode,\r\n application: PermissionApplicationNode,\r\n permissionsByPath: Map<string, ResourcePermission[]>\r\n ): { childResources: ResourceNode[]; nodes: ResourceNode[] } => {\r\n const childResources: ResourceNode[] = [];\r\n const nodes: ResourceNode[] = [];\r\n\r\n section.resources.forEach((resource: PermissionResourceNode) => {\r\n const resourcePath = resource.permissionPath.toLowerCase().replace(/\\.\\*$/, '').replace(/\\*$/, '');\r\n const resourcePermissions = permissionsByPath.get(resourcePath) || [];\r\n\r\n const resourceNode: ResourceNode = {\r\n id: resource.id,\r\n path: resource.permissionPath,\r\n label: resource.label,\r\n applicationLabel: application.label,\r\n moduleLabel: resource.label,\r\n parentModuleLabel: module.label,\r\n parentModuleId: module.id,\r\n parentSectionLabel: section.label,\r\n parentSectionId: section.id,\r\n level: 'resource',\r\n permissions: resourcePermissions,\r\n totalPermissions: resourcePermissions.length,\r\n };\r\n childResources.push(resourceNode);\r\n nodes.push(resourceNode);\r\n });\r\n\r\n return { childResources, nodes };\r\n };\r\n\r\n const buildSectionNodes = (\r\n module: PermissionModuleNode,\r\n application: PermissionApplicationNode,\r\n permissionsByPath: Map<string, ResourcePermission[]>\r\n ): { childSections: ResourceNode[]; nodes: ResourceNode[] } => {\r\n const childSections: ResourceNode[] = [];\r\n const nodes: ResourceNode[] = [];\r\n\r\n module.sections.forEach((section: PermissionSectionNode) => {\r\n const sectionPath = section.permissionPath.toLowerCase().replace(/\\.\\*$/, '').replace(/\\*$/, '');\r\n const sectionPermissions = permissionsByPath.get(sectionPath) || [];\r\n\r\n const { childResources, nodes: resourceNodes } = buildResourceNodes(\r\n section,\r\n module,\r\n application,\r\n permissionsByPath\r\n );\r\n\r\n const resourcePermissionsCount = childResources.reduce((sum, r) => sum + r.totalPermissions, 0);\r\n const sectionTotalPermissions = sectionPermissions.length + resourcePermissionsCount;\r\n\r\n const sectionNode: ResourceNode = {\r\n id: section.id,\r\n path: section.permissionPath,\r\n label: section.label,\r\n applicationLabel: application.label,\r\n moduleLabel: section.label,\r\n parentModuleLabel: module.label,\r\n parentModuleId: module.id,\r\n level: 'section',\r\n permissions: sectionPermissions,\r\n totalPermissions: sectionTotalPermissions,\r\n childResources: childResources.length > 0 ? childResources : undefined,\r\n };\r\n\r\n childSections.push(sectionNode);\r\n nodes.push(sectionNode);\r\n nodes.push(...resourceNodes);\r\n });\r\n\r\n return { childSections, nodes };\r\n };\r\n\r\n const processApplicationModule = (\r\n module: PermissionModuleNode,\r\n application: PermissionApplicationNode,\r\n permissionsByPath: Map<string, ResourcePermission[]>,\r\n nodes: ResourceNode[]\r\n ): void => {\r\n const modulePath = module.permissionPath.toLowerCase().replace(/\\.\\*$/, '').replace(/\\*$/, '');\r\n const modulePermissions = permissionsByPath.get(modulePath) || [];\r\n\r\n const { childSections, nodes: sectionNodes } = buildSectionNodes(\r\n module,\r\n application,\r\n permissionsByPath\r\n );\r\n\r\n const sectionPermissionsCount = childSections.reduce((sum, s) => sum + s.totalPermissions, 0);\r\n const moduleTotalPermissions = modulePermissions.length + sectionPermissionsCount;\r\n\r\n nodes.push({\r\n id: module.id,\r\n path: module.permissionPath,\r\n label: module.label,\r\n applicationLabel: application.label,\r\n moduleLabel: module.label,\r\n level: 'module',\r\n permissions: modulePermissions,\r\n totalPermissions: moduleTotalPermissions,\r\n childSections: childSections.length > 0 ? childSections : undefined,\r\n });\r\n nodes.push(...sectionNodes);\r\n };\r\n\r\n const processApplication = (\r\n application: PermissionApplicationNode,\r\n permissionsByPath: Map<string, ResourcePermission[]>,\r\n nodes: ResourceNode[]\r\n ): void => {\r\n application.modules.forEach((module: PermissionModuleNode) => {\r\n processApplicationModule(module, application, permissionsByPath, nodes);\r\n });\r\n };\r\n\r\n const flattenPermissionTree = useCallback((tree: PermissionTreeDto, permissionsList: PermissionListDto[]): ResourceNode[] => {\r\n const nodes: ResourceNode[] = [];\r\n\r\n if (!tree || !tree.applications) {\r\n return nodes;\r\n }\r\n\r\n if (!permissionsList || !Array.isArray(permissionsList)) {\r\n return nodes;\r\n }\r\n\r\n const permissionsByPath = new Map<string, ResourcePermission[]>();\r\n permissionsList.forEach(perm => {\r\n const pathLower = perm.path.toLowerCase();\r\n const parts = pathLower.split('.');\r\n if (parts.length >= 2) {\r\n const basePath = parts.slice(0, -1).join('.');\r\n const existing = permissionsByPath.get(basePath) || [];\r\n existing.push({\r\n id: perm.id,\r\n action: parts.at(-1)!,\r\n path: perm.path,\r\n });\r\n permissionsByPath.set(basePath, existing);\r\n }\r\n });\r\n\r\n tree.applications.forEach(application => {\r\n processApplication(application, permissionsByPath, nodes);\r\n });\r\n\r\n return nodes.sort((a, b) => {\r\n const appCompare = a.applicationLabel.localeCompare(b.applicationLabel);\r\n if (appCompare !== 0) return appCompare;\r\n return a.moduleLabel.localeCompare(b.moduleLabel);\r\n });\r\n }, []);\r\n\r\n const loadData = useCallback(async () => {\r\n try {\r\n setLoading(true);\r\n setError(null);\r\n\r\n // Single API call to fetch all matrix data\r\n const matrixData = await adminApi.permissions.getMatrix();\r\n\r\n const assignmentsMap: RolePermissions = {};\r\n matrixData.roles.forEach(role => {\r\n assignmentsMap[role.id] = new Set(role.permissionIds);\r\n });\r\n\r\n const permMap = new Map(matrixData.permissions.map(p => [p.id, p]));\r\n const resourceNodes = flattenPermissionTree(matrixData.tree, matrixData.permissions);\r\n const inheritance = calculateInheritanceMap(assignmentsMap, permMap);\r\n\r\n setRoles(matrixData.roles);\r\n setResources(resourceNodes);\r\n setPermissions(permMap);\r\n setAssignments(assignmentsMap);\r\n setInheritanceMap(inheritance);\r\n\r\n const originalAssignmentsClone = Object.entries(assignmentsMap).reduce((acc: RolePermissions, [roleId, permSet]) => {\r\n acc[roleId] = new Set(Array.from(permSet));\r\n return acc;\r\n }, {});\r\n setOriginalAssignments(originalAssignmentsClone);\r\n changesRef.current.clear();\r\n } catch (err) {\r\n setError(err instanceof Error ? err.message : 'Failed to load permission matrix data');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [flattenPermissionTree, calculateInheritanceMap]);\r\n\r\n // Reload data when language changes (labels are translated by API)\r\n useEffect(() => {\r\n loadData();\r\n }, [loadData, i18n.language]);\r\n\r\n useEffect(() => {\r\n if (permissions.size > 0) {\r\n const inheritance = calculateInheritanceMap(assignments, permissions);\r\n setInheritanceMap(inheritance);\r\n }\r\n }, [assignments, permissions, calculateInheritanceMap]);\r\n\r\n const saveChanges = useCallback(async () => {\r\n if (changesRef.current.size === 0) return;\r\n\r\n const changesToSave = new Map(changesRef.current);\r\n changesRef.current.clear();\r\n setSaving(true);\r\n setSaveError(null);\r\n\r\n try {\r\n const updatePromises = Array.from(changesToSave.entries()).map(async ([roleId, permissionIds]) => {\r\n const role = roles.find(r => r.id === roleId);\r\n if (!role) return;\r\n\r\n await adminApi.roles.update(roleId, {\r\n name: role.name,\r\n shortName: role.shortName,\r\n category: role.category,\r\n description: role.description || undefined,\r\n permissionIds: Array.from(permissionIds),\r\n });\r\n });\r\n\r\n await Promise.all(updatePromises);\r\n\r\n // Deep clone assignments and convert back to Sets\r\n const clonedData = JSON.parse(JSON.stringify(Object.fromEntries(\r\n Object.entries(assignments).map(([k, v]) => [k, Array.from(v)])\r\n ))) as Record<string, string[]>;\r\n\r\n const newOriginal = Object.entries(clonedData).reduce((acc: RolePermissions, [k, v]) => {\r\n acc[k] = new Set(v);\r\n return acc;\r\n }, {});\r\n\r\n setOriginalAssignments(newOriginal);\r\n\r\n // Show success feedback\r\n setSaveSuccess(true);\r\n\r\n // Hide success message after 3 seconds\r\n setTimeout(() => {\r\n setSaveSuccess(false);\r\n }, 3000);\r\n } catch (err: unknown) {\r\n console.error('Failed to save permission changes:', err);\r\n\r\n // Extract error message from API response\r\n let errorMessage = 'Failed to save changes';\r\n if (err && typeof err === 'object' && 'response' in err) {\r\n const response = (err as { response?: { status?: number; data?: { message?: string } } }).response;\r\n if (response?.status === 403) {\r\n errorMessage = t('assignments.noPermissionToEditRole');\r\n } else if (response?.data?.message) {\r\n errorMessage = response.data.message;\r\n }\r\n } else if (err instanceof Error) {\r\n errorMessage = err.message;\r\n }\r\n\r\n setSaveError(errorMessage);\r\n\r\n // Force reload from server to ensure UI is in sync (Fix #6)\r\n // This replaces the local revert which might be out of sync with server state\r\n await loadData();\r\n } finally {\r\n setSaving(false);\r\n }\r\n }, [roles, assignments, loadData]);\r\n\r\n const scheduleAutoSave = useCallback(() => {\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n saveTimeoutRef.current = setTimeout(() => {\r\n saveChanges();\r\n }, 500);\r\n }, [saveChanges]);\r\n\r\n const togglePermission = useCallback((roleId: string, permissionId: string) => {\r\n // Prevent modifications to system roles unless user has permission\r\n const role = roles.find(r => r.id === roleId);\r\n if (role?.isSystem && !canEditSystemRoles) {\r\n return;\r\n }\r\n\r\n setAssignments(prev => {\r\n const rolePerms = new Set(prev[roleId] || []);\r\n if (rolePerms.has(permissionId)) {\r\n rolePerms.delete(permissionId);\r\n } else {\r\n rolePerms.add(permissionId);\r\n }\r\n changesRef.current.set(roleId, rolePerms);\r\n scheduleAutoSave();\r\n return { ...prev, [roleId]: rolePerms };\r\n });\r\n }, [roles, canEditSystemRoles, scheduleAutoSave]);\r\n\r\n const grantAllPermissionsForRole = (rolePerms: Set<string>): void => {\r\n resources.forEach(resource => {\r\n resource.permissions.forEach(perm => {\r\n rolePerms.add(perm.id);\r\n });\r\n });\r\n };\r\n\r\n const toggleAllForRole = useCallback((roleId: string, grant: boolean) => {\r\n // Prevent modifications to system roles unless user has permission\r\n const role = roles.find(r => r.id === roleId);\r\n if (role?.isSystem && !canEditSystemRoles) {\r\n return;\r\n }\r\n\r\n setAssignments(prev => {\r\n const rolePerms = new Set<string>();\r\n if (grant) {\r\n grantAllPermissionsForRole(rolePerms);\r\n }\r\n changesRef.current.set(roleId, rolePerms);\r\n scheduleAutoSave();\r\n return { ...prev, [roleId]: rolePerms };\r\n });\r\n }, [roles, canEditSystemRoles, resources, scheduleAutoSave]);\r\n\r\n const updateRolePermissionsForResource = (\r\n rolePerms: Set<string>,\r\n resourceNode: ResourceNode,\r\n grant: boolean\r\n ): void => {\r\n resourceNode.permissions.forEach(perm => {\r\n if (grant) {\r\n rolePerms.add(perm.id);\r\n } else {\r\n rolePerms.delete(perm.id);\r\n }\r\n });\r\n };\r\n\r\n const toggleAllForResource = useCallback((resourceNode: ResourceNode, grant: boolean) => {\r\n setAssignments(prev => {\r\n const updated = { ...prev };\r\n roles.forEach(role => {\r\n // Skip system roles unless user has permission\r\n if (role.isSystem && !canEditSystemRoles) {\r\n return;\r\n }\r\n\r\n const rolePerms = new Set(updated[role.id] || []);\r\n updateRolePermissionsForResource(rolePerms, resourceNode, grant);\r\n updated[role.id] = rolePerms;\r\n changesRef.current.set(role.id, rolePerms);\r\n });\r\n scheduleAutoSave();\r\n return updated;\r\n });\r\n }, [roles, canEditSystemRoles, scheduleAutoSave]);\r\n\r\n const updateFilters = useCallback((newFilters: Partial<MatrixFilters>) => {\r\n setFilters(prev => ({ ...prev, ...newFilters }));\r\n }, []);\r\n\r\n const resetChanges = useCallback(() => {\r\n const resetAssignments = Object.entries(originalAssignments).reduce((acc: RolePermissions, [roleId, permSet]) => {\r\n acc[roleId] = new Set(Array.from(permSet));\r\n return acc;\r\n }, {});\r\n setAssignments(resetAssignments);\r\n changesRef.current.clear();\r\n if (saveTimeoutRef.current) {\r\n clearTimeout(saveTimeoutRef.current);\r\n }\r\n }, [originalAssignments]);\r\n\r\n const filteredRoles = useMemo(() => {\r\n return roles.filter(role => {\r\n // If no roles selected, show all roles\r\n if (filters.selectedRoleIds.length === 0) {\r\n return true;\r\n }\r\n // Otherwise, only show selected roles\r\n return filters.selectedRoleIds.includes(role.id);\r\n });\r\n }, [roles, filters.selectedRoleIds]);\r\n\r\n const filteredResources = useMemo(() => {\r\n return resources.filter(resource => {\r\n if (filters.resourceSearch && !resource.moduleLabel.toLowerCase().includes(filters.resourceSearch.toLowerCase())) {\r\n return false;\r\n }\r\n if (filters.applicationFilter !== 'all' && resource.applicationLabel !== filters.applicationFilter) {\r\n return false;\r\n }\r\n if (filters.moduleFilter !== 'all' && resource.moduleLabel !== filters.moduleFilter) {\r\n return false;\r\n }\r\n return true;\r\n });\r\n }, [resources, filters]);\r\n\r\n const hasChanges = useMemo(() => {\r\n return Array.from(Object.entries(assignments)).some(([roleId, perms]) => {\r\n const original = originalAssignments[roleId];\r\n if (!original) return perms.size > 0;\r\n if (perms.size !== original.size) return true;\r\n return Array.from(perms).some(p => !original.has(p));\r\n });\r\n }, [assignments, originalAssignments]);\r\n\r\n // Compute unique applications for filter dropdown (from API data)\r\n const applications = useMemo(() => {\r\n const appSet = new Map<string, { app: string; label: string }>();\r\n resources.forEach(r => {\r\n if (!appSet.has(r.applicationLabel)) {\r\n appSet.set(r.applicationLabel, {\r\n app: r.applicationLabel,\r\n label: r.applicationLabel,\r\n });\r\n }\r\n });\r\n return Array.from(appSet.values()).sort((a, b) => a.label.localeCompare(b.label));\r\n }, [resources]);\r\n\r\n // Compute modules for cascading filter (from API data)\r\n // Only include actual module-level resources (not sections or resources)\r\n const modules = useMemo(() => {\r\n return resources\r\n .filter(r => r.level === 'module')\r\n .map(r => ({\r\n app: r.applicationLabel,\r\n module: r.label,\r\n label: r.label,\r\n }))\r\n .sort((a, b) => a.label.localeCompare(b.label));\r\n }, [resources]);\r\n\r\n // Compute unique role categories for filter dropdown\r\n const categories = useMemo(() => {\r\n const categorySet = new Set<RoleCategory>(roles.map(r => r.category));\r\n // Sort in a logical order\r\n const order: RoleCategory[] = ['Global', 'Admin', 'Manager', 'Contributor', 'Viewer', 'Custom'];\r\n return order.filter(c => categorySet.has(c));\r\n }, [roles]);\r\n\r\n const getPermissionInheritance = useCallback((roleId: string, permissionId: string): PermissionInheritanceInfo => {\r\n const roleInheritance = inheritanceMap[roleId];\r\n if (!roleInheritance) {\r\n return { isDirect: false, isInherited: false };\r\n }\r\n return roleInheritance.get(permissionId) || { isDirect: false, isInherited: false };\r\n }, [inheritanceMap]);\r\n\r\n const clearSaveError = useCallback(() => {\r\n setSaveError(null);\r\n }, []);\r\n\r\n return {\r\n loading,\r\n error,\r\n saveError,\r\n saving,\r\n saveSuccess,\r\n roles,\r\n resources,\r\n permissions,\r\n assignments,\r\n inheritanceMap,\r\n filters,\r\n filteredRoles,\r\n filteredResources,\r\n hasChanges,\r\n canEditSystemRoles,\r\n applications,\r\n modules,\r\n categories,\r\n togglePermission,\r\n toggleAllForRole,\r\n toggleAllForResource,\r\n updateFilters,\r\n reloadData: loadData,\r\n resetChanges,\r\n saveChanges,\r\n clearSaveError,\r\n getPermissionInheritance,\r\n };\r\n}\r\n","import { useEffect, useRef, useCallback, useState } from 'react';\r\nimport * as signalR from '@microsoft/signalr';\r\n\r\nexport interface NotificationDto {\r\n id: string;\r\n type: string;\r\n title: string;\r\n message: string;\r\n relatedEntityType: string | null;\r\n relatedEntityId: string | null;\r\n actionUrl: string | null;\r\n isRead: boolean;\r\n readAt: string | null;\r\n createdAt: string;\r\n}\r\n\r\ninterface UseSignalROptions {\r\n onNotification?: (notification: NotificationDto) => void;\r\n onUnreadCountUpdate?: (count: number) => void;\r\n onPermissionsChanged?: () => void;\r\n onConnectionStateChange?: (state: signalR.HubConnectionState) => void;\r\n}\r\n\r\nexport function useSignalR(options: UseSignalROptions = {}): {\n connectionState: signalR.HubConnectionState;\n isConnected: boolean;\n connect: () => Promise<void>;\n disconnect: () => Promise<void>;\n} {\r\n const connectionRef = useRef<signalR.HubConnection | null>(null);\r\n const [connectionState, setConnectionState] = useState<signalR.HubConnectionState>(\r\n signalR.HubConnectionState.Disconnected\r\n );\r\n const optionsRef = useRef(options);\r\n optionsRef.current = options;\r\n\r\n const getToken = useCallback(() => {\r\n return localStorage.getItem('token');\r\n }, []);\r\n\r\n const connect = useCallback(async () => {\r\n const token = getToken();\r\n if (!token) {\r\n return;\r\n }\r\n\r\n if (connectionRef.current?.state === signalR.HubConnectionState.Connected) {\r\n return;\r\n }\r\n\r\n try {\r\n const connection = new signalR.HubConnectionBuilder()\r\n .withUrl(`/hubs/notifications`, {\r\n accessTokenFactory: () => token,\r\n })\r\n .withAutomaticReconnect({\r\n nextRetryDelayInMilliseconds: (retryContext) => {\r\n // Exponential backoff: 0, 2, 10, 30 seconds, then stop\r\n if (retryContext.previousRetryCount === 0) return 0;\r\n if (retryContext.previousRetryCount === 1) return 2000;\r\n if (retryContext.previousRetryCount === 2) return 10000;\r\n if (retryContext.previousRetryCount < 5) return 30000;\r\n return null; // Stop retrying after 5 attempts\r\n },\r\n })\r\n .configureLogging(signalR.LogLevel.Warning)\r\n .build();\r\n\r\n // Handle incoming notifications\r\n connection.on('ReceiveNotification', (notification: NotificationDto) => {\r\n optionsRef.current.onNotification?.(notification);\r\n });\r\n\r\n // Handle unread count updates\r\n connection.on('UnreadCountUpdate', (count: number) => {\r\n optionsRef.current.onUnreadCountUpdate?.(count);\r\n });\r\n\r\n // Handle permissions changes (real-time sync)\r\n connection.on('PermissionsChanged', () => {\r\n optionsRef.current.onPermissionsChanged?.();\r\n });\r\n\r\n // Connection state change handlers\r\n connection.onreconnecting(() => {\r\n setConnectionState(signalR.HubConnectionState.Reconnecting);\r\n optionsRef.current.onConnectionStateChange?.(signalR.HubConnectionState.Reconnecting);\r\n });\r\n\r\n connection.onreconnected(() => {\r\n setConnectionState(signalR.HubConnectionState.Connected);\r\n optionsRef.current.onConnectionStateChange?.(signalR.HubConnectionState.Connected);\r\n });\r\n\r\n connection.onclose(() => {\r\n setConnectionState(signalR.HubConnectionState.Disconnected);\r\n optionsRef.current.onConnectionStateChange?.(signalR.HubConnectionState.Disconnected);\r\n });\r\n\r\n await connection.start();\r\n connectionRef.current = connection;\r\n setConnectionState(signalR.HubConnectionState.Connected);\r\n optionsRef.current.onConnectionStateChange?.(signalR.HubConnectionState.Connected);\r\n } catch (error) {\r\n console.error('SignalR: Failed to connect', error);\r\n setConnectionState(signalR.HubConnectionState.Disconnected);\r\n }\r\n }, [getToken]);\r\n\r\n const disconnect = useCallback(async () => {\r\n if (connectionRef.current) {\r\n try {\r\n await connectionRef.current.stop();\r\n connectionRef.current = null;\r\n setConnectionState(signalR.HubConnectionState.Disconnected);\r\n } catch (error) {\r\n console.error('SignalR: Failed to disconnect', error);\r\n }\r\n }\r\n }, []);\r\n\r\n // Connect on mount, disconnect on unmount\r\n useEffect(() => {\r\n connect();\r\n\r\n return () => {\r\n disconnect();\r\n };\r\n }, [connect, disconnect]);\r\n\r\n // Reconnect when token changes (e.g., after login)\r\n useEffect(() => {\r\n const handleStorageChange = (e: StorageEvent) => {\r\n if (e.key === 'token') {\r\n if (e.newValue) {\r\n connect();\r\n } else {\r\n disconnect();\r\n }\r\n }\r\n };\r\n\r\n window.addEventListener('storage', handleStorageChange);\r\n return () => window.removeEventListener('storage', handleStorageChange);\r\n }, [connect, disconnect]);\r\n\r\n return {\r\n connectionState,\r\n isConnected: connectionState === signalR.HubConnectionState.Connected,\r\n connect,\r\n disconnect,\r\n };\r\n}\r\n","import { useCallback, useRef } from 'react';\r\nimport type { HubConnectionState } from '@microsoft/signalr';\r\nimport { useSignalR } from './useSignalR';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { useNavigation } from '@/contexts/NavigationContext';\r\n\r\nexport function usePermissionSync(): {\r\n isConnected: boolean;\r\n connectionState: HubConnectionState;\r\n isAuthenticated: boolean;\r\n} {\r\n const { refreshPermissions, user } = useAuth();\r\n const { refreshMenu } = useNavigation();\r\n const isRefreshingRef = useRef(false);\r\n\r\n const handlePermissionsChanged = useCallback(async () => {\r\n if (isRefreshingRef.current) return;\r\n isRefreshingRef.current = true;\r\n\r\n try {\r\n await Promise.all([\r\n refreshPermissions(),\r\n refreshMenu(),\r\n ]);\r\n } catch (error) {\r\n console.error('[PermissionSync] Failed to refresh permissions:', error);\r\n } finally {\r\n isRefreshingRef.current = false;\r\n }\r\n }, [refreshPermissions, refreshMenu]);\r\n\r\n const { isConnected, connectionState } = useSignalR({\r\n onPermissionsChanged: handlePermissionsChanged,\r\n });\r\n\r\n return {\r\n isConnected,\r\n connectionState,\r\n isAuthenticated: !!user,\r\n };\r\n}\r\n","import { useEffect, useRef, useCallback, useState } from 'react';\r\nimport * as signalR from '@microsoft/signalr';\r\n\r\nexport interface TicketCommentDto {\r\n id: string;\r\n ticketId: string;\r\n userName: string;\r\n content: string;\r\n isInternal: boolean;\r\n isEdited: boolean;\r\n createdAt: string;\r\n}\r\n\r\nexport interface TicketUpdateDto {\r\n ticketId: string;\r\n updateType: string;\r\n oldValue?: string;\r\n newValue?: string;\r\n changedByName?: string;\r\n changedAt: string;\r\n}\r\n\r\ninterface UseTicketSignalROptions {\r\n ticketId: string | undefined;\r\n onCommentAdded?: (comment: TicketCommentDto) => void;\r\n onTicketUpdated?: (update: TicketUpdateDto) => void;\r\n onConnectionStateChange?: (state: signalR.HubConnectionState) => void;\r\n}\r\n\r\nexport function useTicketSignalR(options: UseTicketSignalROptions): {\n connectionState: signalR.HubConnectionState;\n isConnected: boolean;\n joinTicket: (id: string) => Promise<void>;\n leaveTicket: (id: string) => Promise<void>;\n} {\r\n const { ticketId, onCommentAdded, onTicketUpdated, onConnectionStateChange } = options;\r\n const connectionRef = useRef<signalR.HubConnection | null>(null);\r\n const currentTicketRef = useRef<string | null>(null);\r\n const [connectionState, setConnectionState] = useState<signalR.HubConnectionState>(\r\n signalR.HubConnectionState.Disconnected\r\n );\r\n\r\n const getToken = useCallback(() => {\r\n return localStorage.getItem('token');\r\n }, []);\r\n\r\n const joinTicket = useCallback(async (id: string) => {\r\n if (connectionRef.current?.state === signalR.HubConnectionState.Connected) {\r\n try {\r\n await connectionRef.current.invoke('JoinTicket', id);\r\n currentTicketRef.current = id;\r\n } catch (error) {\r\n console.error('SignalR: Failed to join ticket group', error);\r\n }\r\n }\r\n }, []);\r\n\r\n const leaveTicket = useCallback(async (id: string) => {\r\n if (connectionRef.current?.state === signalR.HubConnectionState.Connected) {\r\n try {\r\n await connectionRef.current.invoke('LeaveTicket', id);\r\n } catch (error) {\r\n console.error('SignalR: Failed to leave ticket group', error);\r\n }\r\n }\r\n currentTicketRef.current = null;\r\n }, []);\r\n\r\n const connect = useCallback(async () => {\r\n const token = getToken();\r\n if (!token) {\r\n return;\r\n }\r\n\r\n if (connectionRef.current?.state === signalR.HubConnectionState.Connected) {\r\n return connectionRef.current;\r\n }\r\n\r\n try {\r\n const connection = new signalR.HubConnectionBuilder()\r\n .withUrl(`/hubs/notifications`, {\r\n accessTokenFactory: () => token,\r\n })\r\n .withAutomaticReconnect({\r\n nextRetryDelayInMilliseconds: (retryContext) => {\r\n if (retryContext.previousRetryCount === 0) return 0;\r\n if (retryContext.previousRetryCount === 1) return 2000;\r\n if (retryContext.previousRetryCount === 2) return 10000;\r\n if (retryContext.previousRetryCount < 5) return 30000;\r\n return null;\r\n },\r\n })\r\n .configureLogging(signalR.LogLevel.Warning)\r\n .build();\r\n\r\n // Handle ticket-specific events\r\n connection.on('TicketCommentAdded', (comment: TicketCommentDto) => {\r\n onCommentAdded?.(comment);\r\n });\r\n\r\n connection.on('TicketUpdated', (update: TicketUpdateDto) => {\r\n onTicketUpdated?.(update);\r\n });\r\n\r\n connection.onreconnecting(() => {\r\n setConnectionState(signalR.HubConnectionState.Reconnecting);\r\n onConnectionStateChange?.(signalR.HubConnectionState.Reconnecting);\r\n });\r\n\r\n connection.onreconnected(async () => {\r\n setConnectionState(signalR.HubConnectionState.Connected);\r\n onConnectionStateChange?.(signalR.HubConnectionState.Connected);\r\n\r\n // Re-join ticket group after reconnection\r\n if (currentTicketRef.current) {\r\n await joinTicket(currentTicketRef.current);\r\n }\r\n });\r\n\r\n connection.onclose(() => {\r\n setConnectionState(signalR.HubConnectionState.Disconnected);\r\n onConnectionStateChange?.(signalR.HubConnectionState.Disconnected);\r\n });\r\n\r\n await connection.start();\r\n connectionRef.current = connection;\r\n setConnectionState(signalR.HubConnectionState.Connected);\r\n onConnectionStateChange?.(signalR.HubConnectionState.Connected);\r\n\r\n return connection;\r\n } catch (error) {\r\n console.error('SignalR: Failed to connect', error);\r\n setConnectionState(signalR.HubConnectionState.Disconnected);\r\n return null;\r\n }\r\n }, [getToken, joinTicket, onCommentAdded, onTicketUpdated, onConnectionStateChange]);\r\n\r\n const disconnect = useCallback(async () => {\r\n if (currentTicketRef.current) {\r\n await leaveTicket(currentTicketRef.current);\r\n }\r\n if (connectionRef.current) {\r\n try {\r\n await connectionRef.current.stop();\r\n connectionRef.current = null;\r\n setConnectionState(signalR.HubConnectionState.Disconnected);\r\n } catch (error) {\r\n console.error('SignalR: Failed to disconnect', error);\r\n }\r\n }\r\n }, [leaveTicket]);\r\n\r\n // Connect and join ticket on mount\r\n useEffect(() => {\r\n let isMounted = true;\r\n\r\n const setup = async () => {\r\n await connect();\r\n if (isMounted && ticketId) {\r\n await joinTicket(ticketId);\r\n }\r\n };\r\n\r\n setup();\r\n\r\n return () => {\r\n isMounted = false;\r\n disconnect();\r\n };\r\n }, [connect, disconnect, ticketId, joinTicket]);\r\n\r\n // Handle ticket change\r\n useEffect(() => {\r\n const handleTicketChange = async () => {\r\n // Leave old ticket\r\n if (currentTicketRef.current && currentTicketRef.current !== ticketId) {\r\n await leaveTicket(currentTicketRef.current);\r\n }\r\n // Join new ticket\r\n if (ticketId && ticketId !== currentTicketRef.current) {\r\n await joinTicket(ticketId);\r\n }\r\n };\r\n\r\n if (connectionRef.current?.state === signalR.HubConnectionState.Connected) {\r\n handleTicketChange();\r\n }\r\n }, [ticketId, joinTicket, leaveTicket]);\r\n\r\n return {\r\n connectionState,\r\n isConnected: connectionState === signalR.HubConnectionState.Connected,\r\n joinTicket,\r\n leaveTicket,\r\n };\r\n}\r\n","import { useMemo } from 'react';\r\nimport { useNavigation, type ApplicationDto } from '@/contexts/NavigationContext';\r\nimport { PageRegistry } from '@/extensions/PageRegistry';\r\n\r\n/**\r\n * Route configuration entry derived from the navigation menu API.\r\n * Each entry represents a resolvable route with its component and metadata.\r\n */\r\nexport interface RouteConfigEntry {\r\n /** Hierarchical component key (e.g., \"administration.users\") */\r\n componentKey: string;\r\n /** Permission path for authorization (same as componentKey) */\r\n permissionPath: string;\r\n /** URL route path from DB (e.g., \"/administration/users\") */\r\n route: string;\r\n /** Whether the component is registered in PageRegistry */\r\n hasComponent: boolean;\r\n /** License feature required (null = always available) */\r\n requiredFeature: string | null;\r\n /** Whether this app is always accessible (no permission check) */\r\n isOpen: boolean;\r\n /** Navigation level: application, module, section, resource */\r\n level: 'application' | 'module' | 'section' | 'resource';\r\n /** Application code (for grouping) */\r\n applicationCode: string;\r\n}\r\n\r\n/**\r\n * Flattened route configuration built from the enriched menu API response.\r\n */\r\nexport interface RouteConfig {\r\n /** All route entries flattened from the navigation hierarchy */\r\n entries: RouteConfigEntry[];\r\n /** Map of componentKey → RouteConfigEntry for O(1) lookup */\r\n byKey: Map<string, RouteConfigEntry>;\r\n /** Whether the route config is still loading */\r\n isLoading: boolean;\r\n /** Grouped by application code */\r\n byApplication: Map<string, RouteConfigEntry[]>;\r\n}\r\n\r\nfunction flattenResourceEntries(\r\n appCode: string,\r\n appIsOpen: boolean,\r\n modRequiredFeature: string | null,\r\n section: ApplicationDto['modules'][0]['sections'][0]\r\n): RouteConfigEntry[] {\r\n const entries: RouteConfigEntry[] = [];\r\n\r\n for (const resource of section.resources) {\r\n if (resource.route) {\r\n entries.push({\r\n componentKey: resource.componentKey,\r\n permissionPath: resource.permissionPath,\r\n route: resource.route,\r\n hasComponent: PageRegistry.has(resource.componentKey),\r\n requiredFeature: modRequiredFeature,\r\n isOpen: appIsOpen,\r\n level: 'resource',\r\n applicationCode: appCode,\r\n });\r\n }\r\n }\r\n\r\n return entries;\r\n}\r\n\r\nfunction flattenSectionEntries(\r\n appCode: string,\r\n appIsOpen: boolean,\r\n modRequiredFeature: string | null,\r\n module: ApplicationDto['modules'][0]\r\n): RouteConfigEntry[] {\r\n const entries: RouteConfigEntry[] = [];\r\n\r\n for (const section of module.sections) {\r\n if (section.route) {\r\n entries.push({\r\n componentKey: section.componentKey,\r\n permissionPath: section.permissionPath,\r\n route: section.route,\r\n hasComponent: PageRegistry.has(section.componentKey),\r\n requiredFeature: modRequiredFeature,\r\n isOpen: appIsOpen,\r\n level: 'section',\r\n applicationCode: appCode,\r\n });\r\n }\r\n\r\n entries.push(...flattenResourceEntries(appCode, appIsOpen, modRequiredFeature, section));\r\n }\r\n\r\n return entries;\r\n}\r\n\r\nfunction flattenModuleEntries(\r\n appCode: string,\r\n appIsOpen: boolean,\r\n application: ApplicationDto\r\n): RouteConfigEntry[] {\r\n const entries: RouteConfigEntry[] = [];\r\n\r\n for (const mod of application.modules) {\r\n if (mod.route) {\r\n entries.push({\r\n componentKey: mod.componentKey,\r\n permissionPath: mod.permissionPath,\r\n route: mod.route,\r\n hasComponent: PageRegistry.has(mod.componentKey),\r\n requiredFeature: mod.requiredFeature,\r\n isOpen: appIsOpen,\r\n level: 'module',\r\n applicationCode: appCode,\r\n });\r\n }\r\n\r\n entries.push(...flattenSectionEntries(appCode, appIsOpen, mod.requiredFeature, mod));\r\n }\r\n\r\n return entries;\r\n}\r\n\r\nfunction flattenNavigation(applications: ApplicationDto[]): RouteConfigEntry[] {\r\n const entries: RouteConfigEntry[] = [];\r\n\r\n for (const app of applications) {\r\n if (app.route) {\r\n entries.push({\r\n componentKey: app.componentKey,\r\n permissionPath: app.permissionPath,\r\n route: app.route,\r\n hasComponent: PageRegistry.has(app.componentKey),\r\n requiredFeature: app.requiredFeature,\r\n isOpen: app.isOpen,\r\n level: 'application',\r\n applicationCode: app.code,\r\n });\r\n }\r\n\r\n entries.push(...flattenModuleEntries(app.code, app.isOpen, app));\r\n }\r\n\r\n return entries;\r\n}\r\n\r\n/**\r\n * Hook that transforms the enriched menu API response into a flat route configuration.\r\n *\r\n * The route config is derived from NavigationContext (which already handles\r\n * fetching, caching, and tenant-based invalidation). No separate API call needed.\r\n *\r\n * @returns RouteConfig with all navigation-driven routes and their metadata\r\n */\r\nexport function useRouteConfig(): RouteConfig {\r\n const { menu, isLoading } = useNavigation();\r\n\r\n return useMemo(() => {\r\n if (!menu || isLoading) {\r\n return {\r\n entries: [],\r\n byKey: new Map(),\r\n isLoading: true,\r\n byApplication: new Map(),\r\n };\r\n }\r\n\r\n const entries = flattenNavigation(menu.applications);\r\n const byKey = new Map(entries.map(e => [e.componentKey, e]));\r\n const byApplication = new Map<string, RouteConfigEntry[]>();\r\n\r\n for (const entry of entries) {\r\n const group = byApplication.get(entry.applicationCode) || [];\r\n group.push(entry);\r\n byApplication.set(entry.applicationCode, group);\r\n }\r\n\r\n return { entries, byKey, isLoading: false, byApplication };\r\n }, [menu, isLoading]);\r\n}\r\n","import { useState, useEffect, useMemo, useCallback } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { adminApi } from '@/services/api/adminApi';\r\nimport { matchesWildcard } from '@/utils/permissions';\r\nimport type { PermissionTreeDto, PermissionListDto, PermissionApplicationNode, PermissionModuleNode, PermissionSectionNode, PermissionResourceNode } from '@/services/api/adminApi';\r\nimport type {\r\n ResourceNode,\r\n ResourcePermission,\r\n RolePermissions,\r\n PermissionInheritanceInfo,\r\n InheritanceMap,\r\n MatrixFilters,\r\n} from '@/components/platform/administration/permissions/types';\r\n\r\nexport interface UseEntityPermissionsOptions<TRole, TEntity = unknown> {\r\n entityId: string;\r\n tenantId?: string;\r\n loadEntity: (id: string) => Promise<TEntity>;\r\n loadRoles: (id: string, tenantId?: string) => Promise<TRole[]>;\r\n}\r\n\r\nexport interface UseEntityPermissionsResult<TRole, TEntity = unknown> {\r\n loading: boolean;\r\n error: string | null;\r\n entity: TEntity | null;\r\n roles: TRole[];\r\n resources: ResourceNode[];\r\n assignments: RolePermissions;\r\n inheritanceMap: InheritanceMap;\r\n effectivePermissions: Set<string>;\r\n filters: MatrixFilters;\r\n filteredResources: ResourceNode[];\r\n applications: { app: string; label: string }[];\r\n modules: { app: string; module: string; label: string }[];\r\n updateFilters: (filters: Partial<MatrixFilters>) => void;\r\n getPermissionInheritance: (roleId: string, permissionId: string) => PermissionInheritanceInfo;\r\n getPermissionSources: (permissionId: string) => TRole[];\r\n reloadData: () => Promise<void>;\r\n isTenantMode: boolean;\r\n}\r\n\r\nconst calculateInheritanceForRoleImpl = (\r\n rolePermissions: Set<string>,\r\n permissionsMap: Map<string, PermissionListDto>,\r\n targetPermissionId: string\r\n): PermissionInheritanceInfo => {\r\n const isDirect = rolePermissions.has(targetPermissionId);\r\n const targetPerm = permissionsMap.get(targetPermissionId);\r\n if (!targetPerm) {\r\n return { isDirect, isInherited: false };\r\n }\r\n\r\n const targetPath = targetPerm.path;\r\n let inheritedFromPath: string | undefined;\r\n let inheritedFromId: string | undefined;\r\n let shortestWildcardLength = Infinity;\r\n\r\n for (const permId of Array.from(rolePermissions)) {\r\n const perm = permissionsMap.get(permId);\r\n if (!perm || !perm.isWildcard || permId === targetPermissionId) continue;\r\n\r\n if (matchesWildcard(perm.path, targetPath)) {\r\n const wildcardPathLength = perm.path.split('.').length;\r\n if (wildcardPathLength < shortestWildcardLength) {\r\n shortestWildcardLength = wildcardPathLength;\r\n inheritedFromPath = perm.path;\r\n inheritedFromId = perm.id;\r\n }\r\n }\r\n }\r\n\r\n const isInherited = inheritedFromPath !== undefined;\r\n return {\r\n isDirect,\r\n isInherited,\r\n inheritedFromPath,\r\n inheritedFromId,\r\n };\r\n};\r\n\r\nconst calculateInheritanceMapImpl = (\r\n assignmentsMap: RolePermissions,\r\n permissionsMap: Map<string, PermissionListDto>\r\n): InheritanceMap => {\r\n const newInheritanceMap: InheritanceMap = {};\r\n\r\n Object.entries(assignmentsMap).forEach(([roleId, rolePermissions]) => {\r\n const roleInheritanceMap = new Map<string, PermissionInheritanceInfo>();\r\n\r\n permissionsMap.forEach((_perm, permId) => {\r\n const inheritanceInfo = calculateInheritanceForRoleImpl(\r\n rolePermissions,\r\n permissionsMap,\r\n permId\r\n );\r\n roleInheritanceMap.set(permId, inheritanceInfo);\r\n });\r\n\r\n newInheritanceMap[roleId] = roleInheritanceMap;\r\n });\r\n\r\n return newInheritanceMap;\r\n};\r\n\r\nconst buildPermissionsByPathImpl = (\r\n permissionsList: PermissionListDto[]\r\n): Map<string, ResourcePermission[]> => {\r\n const permissionsByPath = new Map<string, ResourcePermission[]>();\r\n permissionsList.forEach(perm => {\r\n const pathLower = perm.path.toLowerCase();\r\n const parts = pathLower.split('.');\r\n if (parts.length >= 2) {\r\n const basePath = parts.slice(0, -1).join('.');\r\n const existing = permissionsByPath.get(basePath) || [];\r\n existing.push({ id: perm.id, action: parts.at(-1)!, path: perm.path });\r\n permissionsByPath.set(basePath, existing);\r\n }\r\n });\r\n return permissionsByPath;\r\n};\r\n\r\nconst getPathPermissionsImpl = (\r\n permissionsByPath: Map<string, ResourcePermission[]>,\r\n permPath: string\r\n): ResourcePermission[] => {\r\n const normalizedPath = permPath.toLowerCase().replace(/\\.\\*$/, '').replace(/\\*$/, '');\r\n return permissionsByPath.get(normalizedPath) || [];\r\n};\r\n\r\nexport function useEntityPermissions<TRole extends { id: string }, TEntity = unknown>(\r\n options: UseEntityPermissionsOptions<TRole, TEntity>\r\n): UseEntityPermissionsResult<TRole, TEntity> {\r\n const { entityId, tenantId, loadEntity, loadRoles } = options;\r\n const { i18n } = useTranslation();\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n const [entity, setEntity] = useState<TEntity | null>(null);\r\n const [roles, setRoles] = useState<TRole[]>([]);\r\n const [resources, setResources] = useState<ResourceNode[]>([]);\r\n const [permissions, setPermissions] = useState<Map<string, PermissionListDto>>(new Map());\r\n const [assignments, setAssignments] = useState<RolePermissions>({});\r\n const [inheritanceMap, setInheritanceMap] = useState<InheritanceMap>({});\r\n const [filters, setFilters] = useState<MatrixFilters>({\r\n selectedRoleIds: [],\r\n resourceSearch: '',\r\n applicationFilter: 'all',\r\n moduleFilter: 'all',\r\n });\r\n\r\n const isTenantMode = Boolean(tenantId);\r\n\r\n const addModuleNode = useCallback((\r\n nodes: ResourceNode[],\r\n module: PermissionModuleNode,\r\n application: PermissionApplicationNode,\r\n permissionsByPath: Map<string, ResourcePermission[]>\r\n ): void => {\r\n const modulePermissions = getPathPermissionsImpl(permissionsByPath, module.permissionPath);\r\n nodes.push({\r\n id: module.id,\r\n path: module.permissionPath,\r\n label: module.label,\r\n applicationLabel: application.label,\r\n moduleLabel: module.label,\r\n level: 'module',\r\n permissions: modulePermissions,\r\n totalPermissions: modulePermissions.length,\r\n });\r\n }, []);\r\n\r\n const addSectionAndResources = useCallback((\r\n nodes: ResourceNode[],\r\n section: PermissionSectionNode,\r\n module: PermissionModuleNode,\r\n application: PermissionApplicationNode,\r\n permissionsByPath: Map<string, ResourcePermission[]>\r\n ): void => {\r\n const sectionPermissions = getPathPermissionsImpl(permissionsByPath, section.permissionPath);\r\n\r\n section.resources.forEach((resource: PermissionResourceNode) => {\r\n const resourcePermissions = getPathPermissionsImpl(permissionsByPath, resource.permissionPath);\r\n nodes.push({\r\n id: resource.id,\r\n path: resource.permissionPath,\r\n label: resource.label,\r\n applicationLabel: application.label,\r\n moduleLabel: module.label,\r\n parentModuleLabel: module.label,\r\n parentModuleId: module.id,\r\n parentSectionLabel: section.label,\r\n parentSectionId: section.id,\r\n level: 'resource',\r\n permissions: resourcePermissions,\r\n totalPermissions: resourcePermissions.length,\r\n });\r\n });\r\n\r\n nodes.push({\r\n id: section.id,\r\n path: section.permissionPath,\r\n label: section.label,\r\n applicationLabel: application.label,\r\n moduleLabel: module.label,\r\n parentModuleLabel: module.label,\r\n parentModuleId: module.id,\r\n level: 'section',\r\n permissions: sectionPermissions,\r\n totalPermissions: sectionPermissions.length,\r\n });\r\n }, []);\r\n\r\n const processModuleWithSections = useCallback((\r\n module: PermissionModuleNode,\r\n application: PermissionApplicationNode,\r\n permissionsByPath: Map<string, ResourcePermission[]>,\r\n nodes: ResourceNode[]\r\n ): void => {\r\n addModuleNode(nodes, module, application, permissionsByPath);\r\n module.sections.forEach((section: PermissionSectionNode) => {\r\n addSectionAndResources(nodes, section, module, application, permissionsByPath);\r\n });\r\n }, [addModuleNode, addSectionAndResources]);\r\n\r\n const processApplication = useCallback((\r\n application: PermissionApplicationNode,\r\n permissionsByPath: Map<string, ResourcePermission[]>,\r\n nodes: ResourceNode[]\r\n ): void => {\r\n application.modules.forEach((module: PermissionModuleNode) => {\r\n processModuleWithSections(module, application, permissionsByPath, nodes);\r\n });\r\n }, [processModuleWithSections]);\r\n\r\n const flattenPermissionTree = useCallback((\r\n tree: PermissionTreeDto,\r\n permissionsList: PermissionListDto[]\r\n ): ResourceNode[] => {\r\n if (!tree?.applications || !Array.isArray(permissionsList)) {\r\n return [];\r\n }\r\n\r\n const permissionsByPath = buildPermissionsByPathImpl(permissionsList);\r\n const nodes: ResourceNode[] = [];\r\n\r\n tree.applications.forEach(application => {\r\n processApplication(application, permissionsByPath, nodes);\r\n });\r\n\r\n return nodes.sort((a, b) => {\r\n const appCompare = a.applicationLabel.localeCompare(b.applicationLabel);\r\n if (appCompare !== 0) return appCompare;\r\n return a.moduleLabel.localeCompare(b.moduleLabel);\r\n });\r\n }, [processApplication]);\r\n\r\n const loadData = useCallback(async () => {\r\n try {\r\n setLoading(true);\r\n setError(null);\r\n\r\n const [entityData, rolesData, matrixData] = await Promise.all([\r\n loadEntity(entityId),\r\n loadRoles(entityId, tenantId),\r\n adminApi.permissions.getMatrix(),\r\n ]);\r\n\r\n setEntity(entityData);\r\n setRoles(rolesData);\r\n\r\n const roleIds = new Set(rolesData.map(r => r.id));\r\n const relevantRoles = matrixData.roles.filter(r => roleIds.has(r.id));\r\n const assignmentsMap: RolePermissions = {};\r\n relevantRoles.forEach(role => {\r\n assignmentsMap[role.id] = new Set(role.permissionIds);\r\n });\r\n\r\n const permMap = new Map(matrixData.permissions.map(p => [p.id, p]));\r\n const resourceNodes = flattenPermissionTree(matrixData.tree, matrixData.permissions);\r\n const inheritance = calculateInheritanceMapImpl(assignmentsMap, permMap);\r\n\r\n setResources(resourceNodes);\r\n setPermissions(permMap);\r\n setAssignments(assignmentsMap);\r\n setInheritanceMap(inheritance);\r\n } catch (err) {\r\n setError(err instanceof Error ? err.message : 'Failed to load permissions data');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [entityId, tenantId, loadEntity, loadRoles, flattenPermissionTree]);\r\n\r\n useEffect(() => {\r\n loadData();\r\n }, [loadData, i18n.language]);\r\n\r\n useEffect(() => {\r\n if (permissions.size > 0) {\r\n const inheritance = calculateInheritanceMapImpl(assignments, permissions);\r\n setInheritanceMap(inheritance);\r\n }\r\n }, [assignments, permissions]);\r\n\r\n const effectivePermissions = useMemo(() => {\r\n const effective = new Set<string>();\r\n\r\n Object.entries(inheritanceMap).forEach(([_roleId, roleInheritance]) => {\r\n roleInheritance.forEach((info, permId) => {\r\n if (info.isDirect || info.isInherited) {\r\n effective.add(permId);\r\n }\r\n });\r\n });\r\n\r\n return effective;\r\n }, [inheritanceMap]);\r\n\r\n const matchesResourceSearch = (resource: ResourceNode): boolean => {\r\n if (!filters.resourceSearch) return true;\r\n return resource.label.toLowerCase().includes(filters.resourceSearch.toLowerCase());\r\n };\r\n\r\n const matchesApplicationFilter = (resource: ResourceNode): boolean => {\r\n if (filters.applicationFilter === 'all') return true;\r\n return resource.applicationLabel === filters.applicationFilter;\r\n };\r\n\r\n const matchesModuleFilter = (resource: ResourceNode): boolean => {\r\n if (filters.moduleFilter === 'all') return true;\r\n if (resource.level === 'module') {\r\n return resource.label === filters.moduleFilter;\r\n }\r\n return resource.parentModuleLabel === filters.moduleFilter;\r\n };\r\n\r\n const filteredResources = useMemo(() => {\r\n return resources.filter(resource =>\r\n matchesResourceSearch(resource) &&\r\n matchesApplicationFilter(resource) &&\r\n matchesModuleFilter(resource)\r\n );\r\n }, [resources, filters]);\r\n\r\n const applications = useMemo(() => {\r\n const appSet = new Map<string, { app: string; label: string }>();\r\n resources.forEach(r => {\r\n if (!appSet.has(r.applicationLabel)) {\r\n appSet.set(r.applicationLabel, {\r\n app: r.applicationLabel,\r\n label: r.applicationLabel,\r\n });\r\n }\r\n });\r\n return Array.from(appSet.values()).sort((a, b) => a.label.localeCompare(b.label));\r\n }, [resources]);\r\n\r\n const modules = useMemo(() => {\r\n return resources\r\n .filter(r => r.level === 'module')\r\n .map(r => ({\r\n app: r.applicationLabel,\r\n module: r.label,\r\n label: r.label,\r\n }))\r\n .sort((a, b) => a.label.localeCompare(b.label));\r\n }, [resources]);\r\n\r\n const updateFilters = useCallback((newFilters: Partial<MatrixFilters>) => {\r\n setFilters(prev => ({ ...prev, ...newFilters }));\r\n }, []);\r\n\r\n const getPermissionInheritance = useCallback((roleId: string, permissionId: string): PermissionInheritanceInfo => {\r\n const roleInheritance = inheritanceMap[roleId];\r\n if (!roleInheritance) {\r\n return { isDirect: false, isInherited: false };\r\n }\r\n return roleInheritance.get(permissionId) || { isDirect: false, isInherited: false };\r\n }, [inheritanceMap]);\r\n\r\n const getPermissionSources = useCallback((permissionId: string): TRole[] => {\r\n const sources: TRole[] = [];\r\n\r\n roles.forEach(role => {\r\n const roleInheritance = inheritanceMap[role.id];\r\n if (roleInheritance) {\r\n const info = roleInheritance.get(permissionId);\r\n if (info && (info.isDirect || info.isInherited)) {\r\n sources.push(role);\r\n }\r\n }\r\n });\r\n\r\n return sources;\r\n }, [roles, inheritanceMap]);\r\n\r\n return {\r\n loading,\r\n error,\r\n entity,\r\n roles,\r\n resources,\r\n assignments,\r\n inheritanceMap,\r\n effectivePermissions,\r\n filters,\r\n filteredResources,\r\n applications,\r\n modules,\r\n updateFilters,\r\n getPermissionInheritance,\r\n getPermissionSources,\r\n reloadData: loadData,\r\n isTenantMode,\r\n };\r\n}\r\n","import { useCallback, useState } from 'react';\r\n\r\nimport { adminApi } from '@/services/api/adminApi';\r\nimport type { RoleDto, UserDetailDto, TenantMemberDto, UserConsolidatedRoleDto } from '@/services/api/adminApi';\r\nimport { useEntityPermissions, type UseEntityPermissionsResult } from './useEntityPermissions';\r\nimport type { PermissionInheritanceInfo } from '@/components/platform/administration/permissions/types';\r\n\r\nexport interface UseUserPermissionsOptions {\r\n userId: string;\r\n /** Optional tenant context - when provided, loads tenant-scoped roles */\r\n tenantId?: string;\r\n}\r\n\r\nexport interface UseUserPermissionsResult {\r\n loading: boolean;\r\n error: string | null;\r\n user: UserDetailDto | null;\r\n userRoles: RoleDto[];\r\n resources: UseEntityPermissionsResult<RoleDto>['resources'];\r\n assignments: UseEntityPermissionsResult<RoleDto>['assignments'];\r\n inheritanceMap: UseEntityPermissionsResult<RoleDto>['inheritanceMap'];\r\n effectivePermissions: UseEntityPermissionsResult<RoleDto>['effectivePermissions'];\r\n filters: UseEntityPermissionsResult<RoleDto>['filters'];\r\n filteredResources: UseEntityPermissionsResult<RoleDto>['filteredResources'];\r\n applications: UseEntityPermissionsResult<RoleDto>['applications'];\r\n modules: UseEntityPermissionsResult<RoleDto>['modules'];\r\n updateFilters: UseEntityPermissionsResult<RoleDto>['updateFilters'];\r\n getPermissionInheritance: (roleId: string, permissionId: string) => PermissionInheritanceInfo;\r\n getPermissionSources: (permissionId: string) => RoleDto[];\r\n reloadData: () => Promise<void>;\r\n /** Whether we're in tenant-scoped mode */\r\n isTenantMode: boolean;\r\n /** Tenant member data (when in tenant mode) */\r\n tenantMember: TenantMemberDto | null;\r\n /** Assign roles to tenant member (when in tenant mode) */\r\n assignTenantRoles: (roleIds: string[]) => Promise<void>;\r\n /** Consolidated roles with tenant/source info (global mode only) */\r\n consolidatedRoles: UserConsolidatedRoleDto[];\r\n}\r\n\r\nexport function useUserPermissions(options: UseUserPermissionsOptions): UseUserPermissionsResult {\r\n const { userId, tenantId } = options;\r\n const [tenantMember, setTenantMember] = useState<TenantMemberDto | null>(null);\r\n const [consolidatedRoles, setConsolidatedRoles] = useState<UserConsolidatedRoleDto[]>([]);\r\n\r\n const isTenantMode = Boolean(tenantId);\r\n\r\n const loadUser = useCallback(async (id: string) => {\r\n return adminApi.users.getById(id);\r\n }, []);\r\n\r\n const loadUserRoles = useCallback(async (id: string, tid?: string): Promise<RoleDto[]> => {\r\n const userData = await adminApi.users.getById(id);\r\n\r\n let rolesToUse: RoleDto[] = [];\r\n\r\n try {\r\n const consolidated = await adminApi.users.getConsolidatedRoles(id, tid);\r\n setConsolidatedRoles(consolidated);\r\n\r\n rolesToUse = consolidated.map(cr => ({\r\n id: cr.roleId,\r\n name: cr.roleName,\r\n shortName: cr.roleShortName || cr.roleName,\r\n category: 'Custom' as const,\r\n description: null,\r\n }));\r\n\r\n if (tid) {\r\n try {\r\n const members = await adminApi.tenants.getMembers(tid);\r\n const member = members.find(m => m.userId === id);\r\n setTenantMember(member || null);\r\n } catch {\r\n setTenantMember(null);\r\n }\r\n } else {\r\n setTenantMember(null);\r\n }\r\n } catch (consolidatedErr) {\r\n setConsolidatedRoles([]);\r\n setTenantMember(null);\r\n rolesToUse = userData.roles;\r\n }\r\n\r\n const uniqueRoles = Array.from(\r\n new Map(rolesToUse.map(role => [role.id, role])).values()\r\n );\r\n\r\n return uniqueRoles;\r\n }, []);\r\n\r\n const baseResult = useEntityPermissions<RoleDto, UserDetailDto>({\r\n entityId: userId,\r\n tenantId,\r\n loadEntity: loadUser,\r\n loadRoles: loadUserRoles,\r\n });\r\n\r\n const user = baseResult.entity;\r\n const userRoles = baseResult.roles;\r\n\r\n const getPermissionSources = useCallback((permissionId: string): RoleDto[] => {\r\n const sources: RoleDto[] = [];\r\n\r\n userRoles.forEach(role => {\r\n const roleInheritance = baseResult.inheritanceMap[role.id];\r\n if (roleInheritance) {\r\n const info = roleInheritance.get(permissionId);\r\n if (info && (info.isDirect || info.isInherited)) {\r\n sources.push(role);\r\n }\r\n }\r\n });\r\n\r\n return sources;\r\n }, [userRoles, baseResult.inheritanceMap]);\r\n\r\n const assignTenantRoles = useCallback(async (roleIds: string[]) => {\r\n if (!isTenantMode || !tenantId || !tenantMember) {\r\n throw new Error('Cannot assign tenant roles: not in tenant mode or no tenant member found');\r\n }\r\n await adminApi.tenants.assignMemberRoles(tenantId, tenantMember.userId, roleIds);\r\n await baseResult.reloadData();\r\n }, [isTenantMode, tenantId, tenantMember, baseResult]);\r\n\r\n return {\r\n loading: baseResult.loading,\r\n error: baseResult.error,\r\n user,\r\n userRoles,\r\n resources: baseResult.resources,\r\n assignments: baseResult.assignments,\r\n inheritanceMap: baseResult.inheritanceMap,\r\n effectivePermissions: baseResult.effectivePermissions,\r\n filters: baseResult.filters,\r\n filteredResources: baseResult.filteredResources,\r\n applications: baseResult.applications,\r\n modules: baseResult.modules,\r\n updateFilters: baseResult.updateFilters,\r\n getPermissionInheritance: baseResult.getPermissionInheritance,\r\n getPermissionSources,\r\n reloadData: baseResult.reloadData,\r\n isTenantMode,\r\n tenantMember,\r\n assignTenantRoles,\r\n consolidatedRoles,\r\n };\r\n}\r\n","import type { ReactElement } from 'react';\nimport { Moon, Sun } from 'lucide-react';\r\nimport { useTheme } from '@/contexts/ThemeContext';\r\n\r\nexport function ThemeSwitcher(): ReactElement {\r\n const { mode, toggleMode } = useTheme();\r\n\r\n // Ensure we always have a valid mode - default to 'light' if undefined\r\n const effectiveMode = mode === 'dark' ? 'dark' : 'light';\r\n const isLight = effectiveMode === 'light';\r\n\r\n return (\r\n <button\r\n onClick={toggleMode}\r\n className=\"relative p-2 bg-[var(--bg-tertiary)] hover:bg-[var(--bg-secondary)] border border-[var(--border-color)] transition-all duration-300 group\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n aria-label={`Basculer vers le mode ${isLight ? 'sombre' : 'clair'}`}\r\n >\r\n <div className=\"relative w-6 h-6\">\r\n <Sun\r\n className={`absolute inset-0 w-6 h-6 text-amber-500 transition-all duration-300 ${\r\n isLight\r\n ? 'opacity-100 rotate-0 scale-100'\r\n : 'opacity-0 rotate-90 scale-0'\r\n }`}\r\n />\r\n <Moon\r\n className={`absolute inset-0 w-6 h-6 text-[var(--color-accent-400)] transition-all duration-300 ${\r\n !isLight\r\n ? 'opacity-100 rotate-0 scale-100'\r\n : 'opacity-0 -rotate-90 scale-0'\r\n }`}\r\n />\r\n </div>\r\n </button>\r\n );\r\n}\r\n","import { useTranslation } from 'react-i18next';\r\nimport { Check } from 'lucide-react';\r\nimport { useState, useRef, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { userApi } from '@/services/api/userApi';\r\nimport { changeLanguage } from '@/i18n/config';\r\n\r\nconst languages = [\r\n { code: 'en', label: 'English', short: 'EN' },\r\n { code: 'fr', label: 'Français', short: 'FR' },\r\n { code: 'de', label: 'Deutsch', short: 'DE' },\r\n { code: 'it', label: 'Italiano', short: 'IT' },\r\n];\r\n\r\nexport function LanguageSwitcher(): ReactElement {\r\n const { i18n } = useTranslation();\r\n const { isAuthenticated } = useAuth();\r\n const [isOpen, setIsOpen] = useState(false);\r\n const dropdownRef = useRef<HTMLDivElement>(null);\r\n\r\n const currentLang = i18n.language?.split('-')[0] || 'en';\r\n const currentLanguage = languages.find((l) => l.code === currentLang) || languages[0];\r\n\r\n const handleSelect = async (code: string) => {\r\n // Change language locally (lazy loads translations if needed)\r\n await changeLanguage(code);\r\n document.documentElement.lang = code;\r\n setIsOpen(false);\r\n\r\n // Save to server if user is authenticated\r\n if (isAuthenticated) {\r\n try {\r\n await userApi.preferences.updateLanguage({ language: code });\r\n } catch {\r\n }\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n const handleClickOutside = (event: MouseEvent) => {\r\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\r\n setIsOpen(false);\r\n }\r\n };\r\n document.addEventListener('mousedown', handleClickOutside);\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, []);\r\n\r\n return (\r\n <div ref={dropdownRef} className=\"relative\">\r\n <button\r\n onClick={() => setIsOpen(!isOpen)}\r\n className=\"relative p-2 bg-[var(--bg-tertiary)] hover:bg-[var(--bg-secondary)] border border-[var(--border-color)] transition-all duration-300\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n aria-label=\"Change language\"\r\n >\r\n <span className=\"flex items-center justify-center w-6 h-6 text-xs font-bold text-[var(--text-secondary)]\">\r\n {currentLanguage.short}\r\n </span>\r\n </button>\r\n\r\n {isOpen && (\r\n <div className=\"absolute right-0 mt-2 py-1 bg-[var(--bg-tertiary)] border border-[var(--border-color)] shadow-lg shadow-[var(--shadow-color)] z-50 overflow-hidden\" style={{ borderRadius: 'var(--radius-card)' }}>\r\n {languages.map((lang) => {\r\n const isSelected = lang.code === currentLang;\r\n return (\r\n <button\r\n key={lang.code}\r\n onClick={() => handleSelect(lang.code)}\r\n className={`w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors whitespace-nowrap ${\r\n isSelected\r\n ? 'bg-primary-600/10 text-primary-500'\r\n : 'text-[var(--text-secondary)] hover:bg-[var(--bg-secondary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n >\r\n <span className=\"text-xs font-bold w-6\">{lang.short}</span>\r\n <span className=\"text-sm font-medium\">{lang.label}</span>\r\n {isSelected && <Check className=\"w-4 h-4 ml-auto text-primary-500\" />}\r\n </button>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n","/**\r\n * Tenant URL Utilities\r\n *\r\n * Provides URL building functions for multi-tenant navigation.\r\n * In multi-tenant mode, URLs include /t/{slug} prefix.\r\n * In single-tenant mode, URLs are standard paths.\r\n */\r\n\r\nimport { useCallback } from 'react';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { useFeatureConfig } from '@/contexts/FeatureConfigContext';\r\n\r\n/**\r\n * Builds a URL with optional tenant slug prefix.\r\n * Without URL routing (single-tenant or hybrid) or without slug, returns the path as-is.\r\n * With URL routing and a tenant, prefixes with /t/{slug}\r\n */\r\nexport function buildTenantUrl(\r\n path: string,\r\n options: {\r\n slug?: string | null;\r\n useUrlRouting: boolean;\r\n }\r\n): string {\r\n // No prefix needed without URL routing or without slug\r\n if (!options.useUrlRouting || !options.slug) {\r\n // Remove any existing /t/ prefix if present\r\n return stripSlugFromPath(path);\r\n }\r\n\r\n // Already has correct prefix\r\n if (path.startsWith(`/t/${options.slug}`)) {\r\n return path;\r\n }\r\n\r\n // Remove any existing /t/ prefix and add the new one\r\n const cleanPath = stripSlugFromPath(path);\r\n return `/t/${options.slug}${cleanPath}`;\r\n}\r\n\r\n/**\r\n * Extracts the tenant slug from a URL path.\r\n * Returns null if no slug is present.\r\n */\r\nexport function extractSlugFromPath(path: string): string | null {\r\n const match = /^\\/t\\/([^/]+)/.exec(path);\r\n return match ? match[1] : null;\r\n}\r\n\r\n/**\r\n * Removes the tenant slug prefix from a URL path.\r\n */\r\nexport function stripSlugFromPath(path: string): string {\r\n const stripped = path.replace(/^\\/t\\/[^/]+/, '');\r\n return stripped || '/';\r\n}\r\n\r\n/**\r\n * Hook for building tenant-aware URLs in components.\r\n * Automatically uses current tenant context.\r\n */\r\nexport function useTenantUrl(): (path: string) => string {\r\n const { currentTenant } = useTenant();\r\n const { useUrlRouting } = useFeatureConfig();\r\n\r\n return useCallback(\r\n (path: string) => {\r\n return buildTenantUrl(path, {\r\n slug: currentTenant?.slug,\r\n useUrlRouting,\r\n });\r\n },\r\n [currentTenant?.slug, useUrlRouting]\r\n );\r\n}\r\n\r\n/**\r\n * Hook that provides both the URL builder and current slug info.\r\n */\r\nexport function useTenantNavigation(): {\r\n buildUrl: (path: string) => string;\r\n currentSlug: string | null;\r\n useUrlRouting: boolean;\r\n shouldShowSlugInUrl: boolean;\r\n} {\r\n const { currentTenant } = useTenant();\r\n const { useUrlRouting } = useFeatureConfig();\r\n const buildUrl = useTenantUrl();\r\n\r\n return {\r\n buildUrl,\r\n currentSlug: currentTenant?.slug ?? null,\r\n useUrlRouting,\r\n shouldShowSlugInUrl: useUrlRouting && !!currentTenant,\r\n };\r\n}\r\n","import { useState, useRef, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useLocation, useNavigate } from 'react-router-dom';\r\nimport {\r\n User,\r\n LogOut,\r\n ChevronDown,\r\n LayoutGrid,\r\n RefreshCw,\r\n} from 'lucide-react';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { useNavigation } from '@/contexts/NavigationContext';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { useTenantUrl } from '@/utils/tenantUrl';\r\nimport { api } from '@/services/api/apiClient';\r\n\r\nconst userAvatarGradient: React.CSSProperties = {\r\n background: 'linear-gradient(to bottom right, var(--color-accent-500), var(--color-accent-600))',\r\n};\r\n\r\nexport function AvatarMenu(): ReactElement {\r\n const { t } = useTranslation('navigation');\r\n const { user, logout } = useAuth();\r\n const { refreshMenu } = useNavigation();\r\n const { refreshTenants } = useTenant();\r\n const location = useLocation();\r\n const navigate = useNavigate();\r\n const buildTenantUrl = useTenantUrl();\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [isRefreshing, setIsRefreshing] = useState(false);\r\n const menuRef = useRef<HTMLDivElement>(null);\r\n\r\n const handleRefreshCache = async () => {\r\n setIsRefreshing(true);\r\n try {\r\n // Invalidate server-side cache (non-blocking - don't prevent client refresh if this fails)\r\n await api.post('/api/navigation/invalidate-cache').catch(() => {});\r\n // Refresh both navigation menu and tenant list for the header\r\n await Promise.all([\r\n refreshMenu(),\r\n refreshTenants()\r\n ]);\r\n } catch (error) {\r\n console.error('Failed to refresh cache:', error);\r\n } finally {\r\n setIsRefreshing(false);\r\n setIsOpen(false);\r\n }\r\n };\r\n\r\n const handleNavigate = (route: string) => {\r\n const targetUrl = buildTenantUrl(route);\r\n if (location.pathname !== targetUrl) {\r\n navigate(targetUrl);\r\n }\r\n setIsOpen(false);\r\n };\r\n\r\n useEffect(() => {\r\n function handleClickOutside(event: MouseEvent) {\r\n if (menuRef.current && !menuRef.current.contains(event.target as Node)) {\r\n setIsOpen(false);\r\n }\r\n }\r\n document.addEventListener('mousedown', handleClickOutside);\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, []);\r\n\r\n useEffect(() => {\r\n function handleEscape(event: KeyboardEvent) {\r\n if (event.key === 'Escape') setIsOpen(false);\r\n }\r\n document.addEventListener('keydown', handleEscape);\r\n return () => document.removeEventListener('keydown', handleEscape);\r\n }, []);\r\n\r\n const getInitials = () => {\r\n if (!user) return '?';\r\n const first = user.firstName?.[0] || '';\r\n const last = user.lastName?.[0] || '';\r\n return (first + last).toUpperCase() || user.email.at(0)?.toUpperCase() || '?';\r\n };\r\n\r\n const getDisplayName = () => {\r\n if (!user) return '';\r\n if (user.firstName || user.lastName) {\r\n return `${user.firstName || ''} ${user.lastName || ''}`.trim();\r\n }\r\n return user.email.split('@')[0];\r\n };\r\n\r\n return (\r\n <div ref={menuRef} className=\"relative\">\r\n <button\r\n onClick={() => setIsOpen(!isOpen)}\r\n className=\"flex items-center gap-2 p-1.5 pr-3 bg-[var(--bg-tertiary)] hover:bg-[var(--bg-secondary)] border border-[var(--border-color)] transition-all duration-200 group\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n aria-expanded={isOpen}\r\n aria-haspopup=\"true\"\r\n >\r\n <div\r\n className=\"w-8 h-8 rounded-full flex items-center justify-center text-white text-sm font-medium shadow-sm\"\r\n style={userAvatarGradient}\r\n >\r\n {getInitials()}\r\n </div>\r\n <ChevronDown className={`w-4 h-4 text-[var(--text-tertiary)] transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`} />\r\n </button>\r\n\r\n <div className={`absolute right-0 mt-2 w-72 origin-top-right transition-all duration-200 ease-out ${\r\n isOpen\r\n ? 'opacity-100 scale-100 translate-y-0'\r\n : 'opacity-0 scale-95 -translate-y-2 pointer-events-none'\r\n }`}>\r\n <div className=\"bg-[var(--bg-card)] border border-[var(--border-color)] shadow-xl overflow-hidden\" style={{ borderRadius: 'var(--radius-card)' }}>\r\n <div className=\"p-4 bg-gradient-to-br from-[var(--bg-secondary)] to-[var(--bg-tertiary)]\">\r\n <div className=\"flex items-center gap-3\">\r\n <div\r\n className=\"w-12 h-12 rounded-full flex items-center justify-center text-white text-lg font-semibold shadow-lg\"\r\n style={userAvatarGradient}\r\n >\r\n {getInitials()}\r\n </div>\r\n <div className=\"flex-1 min-w-0\">\r\n <p className=\"font-semibold text-[var(--text-primary)] truncate\">\r\n {getDisplayName()}\r\n </p>\r\n <p className=\"text-sm text-[var(--text-tertiary)] truncate\">\r\n {user?.email}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"p-2 border-b border-[var(--border-color)]\">\r\n <div className=\"space-y-1\">\r\n <button\r\n onClick={() => handleNavigate('/applications')}\r\n className=\"w-full flex items-center gap-3 px-3 py-2.5 hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <div className=\"w-8 h-8 rounded-full bg-[var(--bg-tertiary)] flex items-center justify-center text-[var(--text-secondary)]\">\r\n <LayoutGrid className=\"w-4 h-4\" />\r\n </div>\r\n <span className=\"font-medium text-[var(--text-primary)]\">\r\n {t('avatarMenu.applications')}\r\n </span>\r\n </button>\r\n <button\r\n onClick={() => handleNavigate('/myspace')}\r\n className=\"w-full flex items-center gap-3 px-3 py-2.5 hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <div className=\"w-8 h-8 rounded-full bg-[var(--bg-tertiary)] flex items-center justify-center text-[var(--text-secondary)]\">\r\n <User className=\"w-4 h-4\" />\r\n </div>\r\n <span className=\"font-medium text-[var(--text-primary)]\">\r\n {t('avatarMenu.mySpace')}\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div className=\"p-2\">\r\n <button\r\n onClick={handleRefreshCache}\r\n disabled={isRefreshing}\r\n className=\"w-full flex items-center gap-3 px-3 py-2.5 hover:bg-[var(--bg-tertiary)] transition-colors disabled:opacity-50\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <div className=\"w-8 h-8 rounded-full bg-[var(--bg-tertiary)] flex items-center justify-center text-[var(--text-secondary)]\">\r\n <RefreshCw className={`w-4 h-4 ${isRefreshing ? 'animate-spin' : ''}`} />\r\n </div>\r\n <span className=\"font-medium text-[var(--text-primary)]\">\r\n {t('avatarMenu.refreshCache')}\r\n </span>\r\n </button>\r\n </div>\r\n\r\n <div className=\"p-2 border-t border-[var(--border-color)]\">\r\n <button\r\n onClick={() => {\r\n setIsOpen(false);\r\n logout();\r\n }}\r\n className=\"w-full flex items-center gap-3 px-3 py-2.5 hover:bg-red-500/10 transition-colors group\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <div className=\"w-8 h-8 rounded-full bg-[var(--bg-tertiary)] group-hover:bg-red-500/20 flex items-center justify-center text-[var(--text-secondary)] group-hover:text-red-500 transition-colors\">\r\n <LogOut className=\"w-4 h-4\" />\r\n </div>\r\n <span className=\"font-medium text-[var(--text-primary)] group-hover:text-red-500 transition-colors\">\r\n {t('avatarMenu.logout')}\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState, useRef, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useNavigate } from 'react-router-dom';\r\nimport {\r\n LayoutGrid,\r\n Star,\r\n Settings,\r\n Users,\r\n Key,\r\n Briefcase,\r\n FolderOpen,\r\n FileText,\r\n Box,\r\n} from 'lucide-react';\r\nimport { useNavigation, type ApplicationDto } from '@/contexts/NavigationContext';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { useTenantUrl } from '@/utils/tenantUrl';\r\n\r\nconst iconMap: Record<string, typeof LayoutGrid> = {\r\n settings: Settings,\r\n users: Users,\r\n key: Key,\r\n briefcase: Briefcase,\r\n folder: FolderOpen,\r\n file: FileText,\r\n box: Box,\r\n};\r\n\r\nfunction getIcon(iconName: string | null) {\r\n if (!iconName) return Box;\r\n return iconMap[iconName.toLowerCase()] || Box;\r\n}\r\n\r\ninterface AppCardProps {\r\n readonly app: ApplicationDto;\r\n readonly isFavorite: boolean;\r\n readonly showFavorite: boolean;\r\n readonly onToggleFavorite: () => void;\r\n readonly onSelect: () => void;\r\n}\r\n\r\nfunction AppCard({ app, isFavorite, showFavorite, onToggleFavorite, onSelect }: AppCardProps) {\r\n const Icon = getIcon(app.icon);\r\n\r\n return (\r\n <div className=\"group relative\">\r\n <button\r\n onClick={onSelect}\r\n className=\"w-full flex flex-col items-center gap-2 p-3 hover:bg-[var(--bg-tertiary)] transition-all duration-200\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <div className=\"w-12 h-12 bg-gradient-to-br from-[var(--color-accent-500)]/20 to-[var(--color-accent-600)]/20 flex items-center justify-center group-hover:from-[var(--color-accent-500)]/30 group-hover:to-[var(--color-accent-600)]/30 transition-all\" style={{ borderRadius: 'var(--radius-card)' }}>\r\n <Icon className=\"w-6 h-6 text-[var(--color-accent-500)]\" />\r\n </div>\r\n <span className=\"text-xs font-medium text-[var(--text-primary)] text-center line-clamp-2\">\r\n {app.label}\r\n </span>\r\n </button>\r\n {showFavorite && (\r\n <button\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n onToggleFavorite();\r\n }}\r\n className={`absolute top-1 right-1 p-1.5 transition-all duration-200 ${\r\n isFavorite\r\n ? 'text-amber-500 bg-amber-500/10'\r\n : 'text-[var(--text-tertiary)] opacity-0 group-hover:opacity-100 hover:bg-[var(--bg-secondary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n title={isFavorite ? 'Retirer des favoris' : 'Ajouter aux favoris'}\r\n >\r\n <Star className={`w-4 h-4 ${isFavorite ? 'fill-current' : ''}`} />\r\n </button>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport function AppSwitcher(): ReactElement {\r\n const { t } = useTranslation('navigation');\r\n const { favorites, menu, toggleFavorite, isFavorite } = useNavigation();\r\n const { currentTenant } = useTenant();\r\n const [isOpen, setIsOpen] = useState(false);\r\n const menuRef = useRef<HTMLDivElement>(null);\r\n const navigate = useNavigate();\r\n const buildTenantUrl = useTenantUrl();\r\n const hasTenant = !!currentTenant;\r\n\r\n const applications = menu?.applications || [];\r\n\r\n useEffect(() => {\r\n function handleClickOutside(event: MouseEvent) {\r\n if (menuRef.current && !menuRef.current.contains(event.target as Node)) {\r\n setIsOpen(false);\r\n }\r\n }\r\n document.addEventListener('mousedown', handleClickOutside);\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, []);\r\n\r\n useEffect(() => {\r\n function handleEscape(event: KeyboardEvent) {\r\n if (event.key === 'Escape') setIsOpen(false);\r\n }\r\n document.addEventListener('keydown', handleEscape);\r\n return () => document.removeEventListener('keydown', handleEscape);\r\n }, []);\r\n\r\n const handleSelectApp = (app: ApplicationDto) => {\r\n setIsOpen(false);\r\n if (app.route) {\r\n navigate(buildTenantUrl(app.route));\r\n }\r\n };\r\n\r\n return (\r\n <div ref={menuRef} className=\"relative\">\r\n <button\r\n onClick={() => setIsOpen(!isOpen)}\r\n className=\"p-2 hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n aria-expanded={isOpen}\r\n aria-haspopup=\"true\"\r\n title={t('appSwitcher.title', 'Applications')}\r\n >\r\n <LayoutGrid className=\"w-5 h-5 text-[var(--text-secondary)]\" />\r\n </button>\r\n\r\n <div className={`absolute right-0 mt-2 w-80 origin-top-right transition-all duration-200 ease-out ${\r\n isOpen\r\n ? 'opacity-100 scale-100 translate-y-0'\r\n : 'opacity-0 scale-95 -translate-y-2 pointer-events-none'\r\n }`}>\r\n <div className=\"bg-[var(--bg-card)] border border-[var(--border-color)] shadow-xl overflow-hidden\" style={{ borderRadius: 'var(--radius-card)' }}>\r\n {favorites.length > 0 && (\r\n <div className=\"p-3 border-b border-[var(--border-color)]\">\r\n <p className=\"px-2 py-1 text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider flex items-center gap-2\">\r\n <Star className=\"w-3 h-3 text-amber-500 fill-amber-500\" />\r\n {t('appSwitcher.favorites', 'Favoris')}\r\n </p>\r\n <div className=\"grid grid-cols-3 gap-1 mt-2\">\r\n {favorites.slice(0, 6).map(app => (\r\n <AppCard\r\n key={app.id}\r\n app={app as ApplicationDto}\r\n isFavorite={true}\r\n showFavorite={hasTenant}\r\n onToggleFavorite={() => toggleFavorite(app.id)}\r\n onSelect={() => {\r\n setIsOpen(false);\r\n if (app.route) navigate(buildTenantUrl(app.route));\r\n }}\r\n />\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n\r\n <div className=\"p-3\">\r\n <p className=\"px-2 py-1 text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider\">\r\n {t('appSwitcher.allApps', 'Toutes les applications')}\r\n </p>\r\n {applications.length > 0 ? (\r\n <div className=\"grid grid-cols-3 gap-1 mt-2 max-h-64 overflow-y-auto\">\r\n {applications.map(app => (\r\n <AppCard\r\n key={app.id}\r\n app={app}\r\n isFavorite={isFavorite(app.id)}\r\n showFavorite={hasTenant}\r\n onToggleFavorite={() => toggleFavorite(app.id)}\r\n onSelect={() => handleSelectApp(app)}\r\n />\r\n ))}\r\n </div>\r\n ) : (\r\n <p className=\"text-sm text-[var(--text-tertiary)] text-center py-6\">\r\n {t('appSwitcher.noApps', 'Aucune application disponible')}\r\n </p>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState, useCallback } from 'react';\r\nimport { adminApi } from '@/services/api/adminApi';\r\n\r\nexport function useTenantFavorites(): {\r\n favorites: string[];\r\n loadFavorites: () => Promise<void>;\r\n toggleFavorite: (tenantId: string) => Promise<void>;\r\n recordRecent: (tenantId: string) => void;\r\n} {\r\n const [favorites, setFavorites] = useState<string[]>([]);\r\n\r\n const loadFavorites = useCallback(async () => {\r\n try {\r\n const result = await adminApi.myTenants.getFavorites();\r\n setFavorites(result);\r\n } catch {\r\n // Silently fail — favorites are non-critical\r\n }\r\n }, []);\r\n\r\n const toggleFavorite = useCallback(async (tenantId: string) => {\r\n const isFav = favorites.includes(tenantId);\r\n try {\r\n if (isFav) {\r\n const newFavorites = await adminApi.myTenants.removeFavorite(tenantId);\r\n setFavorites(newFavorites);\r\n } else {\r\n const newFavorites = await adminApi.myTenants.addFavorite(tenantId);\r\n setFavorites(newFavorites);\r\n }\r\n } catch (err) {\r\n console.error('Failed to toggle favorite:', err);\r\n }\r\n }, [favorites]);\r\n\r\n const recordRecent = useCallback((tenantId: string) => {\r\n adminApi.myTenants.recordRecent(tenantId).catch(console.error);\r\n }, []);\r\n\r\n return {\r\n favorites,\r\n loadFavorites,\r\n toggleFavorite,\r\n recordRecent,\r\n };\r\n}\r\n","import { useState, useRef, useEffect, useMemo, useCallback } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Link } from 'react-router-dom';\r\nimport { Building2, User, ChevronDown, Check, Star, Search, Clock, Eye, Globe } from 'lucide-react';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { useNavigation } from '@/contexts/NavigationContext';\r\nimport { useTheme } from '@/contexts/ThemeContext';\r\nimport type { UserTenantDto } from '@/services/api/adminApi';\r\nimport { useTenantFavorites } from '@/hooks/useTenantFavorites';\r\n\r\nconst statusColors: Record<string, string> = {\r\n Pending: 'bg-blue-500',\r\n Active: 'bg-emerald-500',\r\n Suspended: 'bg-amber-500',\r\n Archived: 'bg-gray-400'\r\n};\r\n\r\nconst statusLabels: Record<string, string> = {\r\n Pending: 'En attente',\r\n Active: 'Actif',\r\n Suspended: 'Suspendu',\r\n Archived: 'Archivé'\r\n};\r\n\r\ninterface TenantItemProps {\r\n readonly tenant: UserTenantDto;\r\n readonly isSelected: boolean;\r\n readonly isFavorite?: boolean;\r\n readonly hasThemeOverride?: boolean;\r\n readonly onSelect: () => void;\r\n readonly onToggleFavorite?: () => void;\r\n readonly showStatus?: boolean;\r\n}\r\n\r\nfunction TenantItem({ tenant, isSelected, isFavorite, hasThemeOverride, onSelect, onToggleFavorite, showStatus = true }: TenantItemProps) {\r\n const isB2B = tenant.type === 'Business';\r\n const isSelectable = tenant.status === 'Active';\r\n\r\n const getButtonClassName = (isSelected: boolean, isSelectable: boolean): string => {\r\n if (isSelected) return 'bg-[var(--color-accent-500)]/10 text-[var(--color-accent-600)]';\r\n if (isSelectable) return 'hover:bg-[var(--bg-tertiary)] text-[var(--text-primary)]';\r\n return 'opacity-50 cursor-not-allowed text-[var(--text-tertiary)]';\r\n };\r\n\r\n return (\r\n <div className=\"flex items-center group\">\r\n <button\r\n onClick={onSelect}\r\n disabled={!isSelectable}\r\n className={`flex-1 flex items-center gap-3 px-3 py-2.5 text-left transition-all duration-150 ${getButtonClassName(isSelected, isSelectable)}`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <div\r\n className={`flex-shrink-0 w-8 h-8 flex items-center justify-center ${\r\n isB2B ? 'bg-blue-500/10' : 'bg-violet-500/10'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-card)' }}\r\n >\r\n {isB2B ? (\r\n <Building2 className=\"w-4 h-4 text-blue-500\" />\r\n ) : (\r\n <User className=\"w-4 h-4 text-violet-500\" />\r\n )}\r\n </div>\r\n\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"flex items-center gap-2\">\r\n <span className=\"font-medium truncate\">{tenant.name}</span>\r\n {hasThemeOverride && (\r\n <span\r\n className=\"w-2 h-2 rounded-full bg-[var(--color-accent-500)]\"\r\n title=\"Thème personnalisé\"\r\n />\r\n )}\r\n </div>\r\n <div className=\"flex items-center gap-2 mt-0.5\">\r\n <span className=\"text-xs text-[var(--text-tertiary)] truncate\">\r\n {tenant.organisationName || tenant.slug}\r\n </span>\r\n {showStatus && (\r\n <>\r\n <span className={`w-1.5 h-1.5 rounded-full ${statusColors[tenant.status]}`} />\r\n {tenant.status !== 'Active' && (\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n {statusLabels[tenant.status]}\r\n </span>\r\n )}\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {isSelected && (\r\n <Check className=\"w-4 h-4 text-[var(--color-accent-500)] flex-shrink-0\" />\r\n )}\r\n </button>\r\n\r\n {onToggleFavorite && isSelectable && (\r\n <button\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n onToggleFavorite();\r\n }}\r\n className={`p-2 transition-colors ${\r\n isFavorite\r\n ? 'text-amber-500'\r\n : 'text-[var(--text-tertiary)] opacity-0 group-hover:opacity-100 hover:text-amber-500'\r\n }`}\r\n title={isFavorite ? 'Retirer des favoris' : 'Ajouter aux favoris'}\r\n >\r\n <Star className={`w-4 h-4 ${isFavorite ? 'fill-amber-500' : ''}`} />\r\n </button>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\n// Local storage keys for recent tenants (fast cache)\r\nconst RECENT_TENANTS_KEY = 'tenant_recents';\r\nconst MAX_RECENT = 5;\r\n\r\nfunction getRecentTenantIds(): string[] {\r\n try {\r\n return JSON.parse(localStorage.getItem(RECENT_TENANTS_KEY) || '[]');\r\n } catch {\r\n return [];\r\n }\r\n}\r\n\r\nfunction addRecentTenant(tenantId: string): void {\r\n const recents = getRecentTenantIds().filter(id => id !== tenantId);\r\n recents.unshift(tenantId);\r\n localStorage.setItem(RECENT_TENANTS_KEY, JSON.stringify(recents.slice(0, MAX_RECENT)));\r\n}\r\n\r\nconst setupOutsideClickListener = (\r\n menuRef: React.RefObject<HTMLDivElement | null>,\r\n setIsOpen: (open: boolean) => void,\r\n setSearch: (search: string) => void\r\n) => {\r\n const handleClickOutside = (event: MouseEvent) => {\r\n if (menuRef.current && !menuRef.current.contains(event.target as Node)) {\r\n setIsOpen(false);\r\n setSearch('');\r\n }\r\n };\r\n document.addEventListener('mousedown', handleClickOutside);\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n};\r\n\r\nconst setupEscapeListener = (\r\n setIsOpen: (open: boolean) => void,\r\n setSearch: (search: string) => void\r\n) => {\r\n const handleEscape = (event: KeyboardEvent) => {\r\n if (event.key === 'Escape') {\r\n setIsOpen(false);\r\n setSearch('');\r\n }\r\n };\r\n document.addEventListener('keydown', handleEscape);\r\n return () => document.removeEventListener('keydown', handleEscape);\r\n};\r\n\r\nconst useTenantCategories = (\r\n filteredTenants: UserTenantDto[],\r\n favorites: string[],\r\n recentIds: string[],\r\n userTenants: UserTenantDto[],\r\n search: string\r\n) => {\r\n const favoriteTenants = useMemo(() =>\r\n filteredTenants.filter(t => favorites.includes(t.id) && t.status === 'Active'),\r\n [filteredTenants, favorites]\r\n );\r\n\r\n const recentTenants = useMemo(() => {\r\n if (search.trim()) return [];\r\n const favSet = new Set(favorites);\r\n return recentIds\r\n .map(id => userTenants.find(t => t.id === id))\r\n .filter((t): t is UserTenantDto => !!t && t.status === 'Active' && !favSet.has(t.id))\r\n .slice(0, MAX_RECENT);\r\n }, [recentIds, userTenants, favorites, search]);\r\n\r\n const otherTenants = useMemo(() => {\r\n const excludedIds = new Set([...favorites, ...recentIds.slice(0, MAX_RECENT)]);\r\n return filteredTenants.filter(t => !excludedIds.has(t.id));\r\n }, [filteredTenants, favorites, recentIds]);\r\n\r\n return { favoriteTenants, recentTenants, otherTenants };\r\n};\r\n\r\nexport function TenantSelector(): ReactElement | null {\r\n const { t } = useTranslation('navigation');\r\n const { refreshMenu } = useNavigation();\r\n const {\r\n currentTenant,\r\n userTenants,\r\n switchTenant,\r\n isLoading,\r\n hasMultipleTenants,\r\n hasGlobalRoles,\r\n isGlobalView,\r\n enterGlobalView\r\n } = useTenant();\r\n const { tenantOverrides, loadTenantOverrides } = useTheme();\r\n\r\n // All hooks must be called before any early return (React rules of hooks)\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [search, setSearch] = useState('');\r\n const [recentIds] = useState<string[]>(() => getRecentTenantIds());\r\n const menuRef = useRef<HTMLDivElement>(null);\r\n const searchInputRef = useRef<HTMLInputElement>(null);\r\n\r\n const { favorites, loadFavorites, toggleFavorite, recordRecent } = useTenantFavorites();\r\n\r\n // Load favorites and tenant overrides from API when opening\r\n useEffect(() => {\r\n if (isOpen) {\r\n loadFavorites();\r\n loadTenantOverrides();\r\n }\r\n }, [isOpen, loadFavorites, loadTenantOverrides]);\r\n\r\n // Focus search when opening\r\n useEffect(() => {\r\n if (isOpen && searchInputRef.current) {\r\n setTimeout(() => searchInputRef.current?.focus(), 100);\r\n }\r\n }, [isOpen]);\r\n\r\n useEffect(() => {\r\n return setupOutsideClickListener(menuRef, setIsOpen, setSearch);\r\n }, []);\r\n\r\n useEffect(() => {\r\n return setupEscapeListener(setIsOpen, setSearch);\r\n }, []);\r\n\r\n const handleSelectTenant = useCallback(async (tenantId: string) => {\r\n if (tenantId === currentTenant?.id && !isGlobalView) {\r\n setIsOpen(false);\r\n return;\r\n }\r\n\r\n try {\r\n // Track recent locally (fast) + in API (persistent)\r\n addRecentTenant(tenantId);\r\n recordRecent(tenantId);\r\n\r\n await switchTenant(tenantId);\r\n // ThemeSync will automatically handle theme scope when currentTenant changes\r\n setIsOpen(false);\r\n setSearch('');\r\n // Refresh navigation data for the new tenant context\r\n await refreshMenu();\r\n } catch (err) {\r\n console.error('Failed to switch tenant:', err);\r\n }\r\n }, [currentTenant?.id, isGlobalView, switchTenant, refreshMenu, recordRecent]);\r\n\r\n const handleEnterGlobalView = useCallback(async () => {\r\n await enterGlobalView();\r\n setIsOpen(false);\r\n setSearch('');\r\n // Refresh navigation data for global view\r\n await refreshMenu();\r\n }, [enterGlobalView, refreshMenu]);\r\n\r\n const handleToggleFavorite = useCallback(async (tenantId: string) => {\r\n await toggleFavorite(tenantId);\r\n }, [toggleFavorite]);\r\n\r\n const filteredTenants = useMemo(() => {\r\n if (!search.trim()) return userTenants;\r\n const searchLower = search.toLowerCase();\r\n return userTenants.filter(t =>\r\n t.name.toLowerCase().includes(searchLower) ||\r\n t.slug.toLowerCase().includes(searchLower) ||\r\n t.organisationName?.toLowerCase().includes(searchLower)\r\n );\r\n }, [userTenants, search]);\r\n\r\n const { favoriteTenants, recentTenants, otherTenants } = useTenantCategories(\r\n filteredTenants, favorites, recentIds, userTenants, search\r\n );\r\n\r\n // Early returns AFTER all hooks (React rules of hooks)\r\n if (isLoading) {\r\n return null;\r\n }\r\n\r\n // Show selector for users with multiple tenants OR global admin roles (can access all tenants)\r\n const showSelector = hasMultipleTenants || hasGlobalRoles;\r\n\r\n if (!showSelector && currentTenant) {\r\n const isB2B = currentTenant.type === 'Business';\r\n return (\r\n <div\r\n className=\"flex items-center gap-2 px-3 py-1.5 bg-[var(--bg-secondary)] border border-[var(--border-color)]\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n >\r\n {isB2B ? (\r\n <Building2 className=\"w-4 h-4 text-blue-500\" />\r\n ) : (\r\n <User className=\"w-4 h-4 text-violet-500\" />\r\n )}\r\n <span className=\"text-sm font-medium text-[var(--text-primary)]\">{currentTenant.name}</span>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div ref={menuRef} className=\"relative\">\r\n <button\r\n onClick={() => setIsOpen(!isOpen)}\r\n className={`flex items-center gap-2 px-3 py-1.5 border transition-colors ${\r\n isGlobalView\r\n ? 'bg-emerald-500/10 border-emerald-500/30 hover:bg-emerald-500/20'\r\n : 'bg-[var(--bg-secondary)] border-[var(--border-color)] hover:bg-[var(--bg-tertiary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n aria-expanded={isOpen}\r\n aria-haspopup=\"true\"\r\n >\r\n {isGlobalView && (\r\n <>\r\n <Globe className=\"w-4 h-4 text-emerald-600\" />\r\n <span className=\"text-sm font-medium text-emerald-700\">\r\n {t('tenants.selector.global', 'Global')}\r\n </span>\r\n </>\r\n )}\r\n {!isGlobalView && currentTenant && (\r\n <>\r\n {currentTenant.type === 'Business' ? (\r\n <Building2 className=\"w-4 h-4 text-blue-500\" />\r\n ) : (\r\n <User className=\"w-4 h-4 text-violet-500\" />\r\n )}\r\n <span className=\"text-sm font-medium text-[var(--text-primary)] max-w-[120px] truncate\">\r\n {currentTenant.name}\r\n </span>\r\n </>\r\n )}\r\n {!isGlobalView && !currentTenant && (\r\n <>\r\n <Building2 className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\r\n {t('tenants.selector.select', 'Select')}\r\n </span>\r\n </>\r\n )}\r\n <ChevronDown className={`w-4 h-4 text-[var(--text-tertiary)] transition-transform ${isOpen ? 'rotate-180' : ''}`} />\r\n </button>\r\n\r\n <div className={`absolute left-0 mt-2 w-80 origin-top-left transition-all duration-200 ease-out z-50 ${\r\n isOpen\r\n ? 'opacity-100 scale-100 translate-y-0'\r\n : 'opacity-0 scale-95 -translate-y-2 pointer-events-none'\r\n }`}>\r\n <div className=\"bg-[var(--bg-card)] border border-[var(--border-color)] shadow-xl overflow-hidden\" style={{ borderRadius: 'var(--radius-card)' }}>\r\n {/* Search Bar */}\r\n <div className=\"p-2 border-b border-[var(--border-color)]\">\r\n <div className=\"relative\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-tertiary)]\" />\r\n <input\r\n ref={searchInputRef}\r\n type=\"text\"\r\n value={search}\r\n onChange={(e) => setSearch(e.target.value)}\r\n placeholder={t('tenants.selector.search', 'Rechercher...')}\r\n className=\"w-full pl-9 pr-3 py-2 text-sm bg-[var(--bg-secondary)] border border-[var(--border-color)] focus:border-[var(--color-accent-500)] focus:outline-none\"\r\n style={{ borderRadius: 'var(--radius-input)' }}\r\n />\r\n </div>\r\n </div>\r\n\r\n <div className=\"max-h-80 overflow-y-auto\">\r\n {/* Global View Option - only for users with global roles */}\r\n {hasGlobalRoles && !search && (\r\n <div className=\"p-2 border-b border-[var(--border-color)]\">\r\n <button\r\n onClick={handleEnterGlobalView}\r\n className={`w-full flex items-center gap-3 px-3 py-2.5 text-left transition-all duration-150 ${\r\n isGlobalView\r\n ? 'bg-emerald-500/10 text-emerald-700'\r\n : 'hover:bg-[var(--bg-tertiary)] text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <div\r\n className=\"flex-shrink-0 w-8 h-8 bg-emerald-500/10 flex items-center justify-center\"\r\n style={{ borderRadius: 'var(--radius-card)' }}\r\n >\r\n <Globe className=\"w-4 h-4 text-emerald-600\" />\r\n </div>\r\n <div className=\"flex-1 min-w-0\">\r\n <span className=\"font-medium\">{t('tenants.selector.global', 'Global')}</span>\r\n <p className=\"text-xs text-[var(--text-tertiary)] mt-0.5\">\r\n {t('tenants.selector.globalDesc', 'Voir tous les tenants')}\r\n </p>\r\n </div>\r\n {isGlobalView && (\r\n <Check className=\"w-4 h-4 text-emerald-600 flex-shrink-0\" />\r\n )}\r\n </button>\r\n </div>\r\n )}\r\n\r\n {/* Favorites Section */}\r\n {favoriteTenants.length > 0 && (\r\n <div className=\"p-2\">\r\n <p className=\"px-2 py-1 text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider flex items-center gap-1.5\">\r\n <Star className=\"w-3 h-3 text-amber-500\" />\r\n {t('tenants.selector.favorites', 'Favoris')}\r\n </p>\r\n <div className=\"mt-1\">\r\n {favoriteTenants.map(tenant => (\r\n <TenantItem\r\n key={tenant.id}\r\n tenant={tenant}\r\n isSelected={!isGlobalView && currentTenant?.id === tenant.id}\r\n isFavorite={true}\r\n hasThemeOverride={tenantOverrides.includes(tenant.id)}\r\n onSelect={() => handleSelectTenant(tenant.id)}\r\n onToggleFavorite={() => handleToggleFavorite(tenant.id)}\r\n />\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Recent Section */}\r\n {recentTenants.length > 0 && (\r\n <div className={`p-2 ${favoriteTenants.length > 0 ? 'border-t border-[var(--border-color)]' : ''}`}>\r\n <p className=\"px-2 py-1 text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider flex items-center gap-1.5\">\r\n <Clock className=\"w-3 h-3\" />\r\n {t('tenants.selector.recent', 'Récents')}\r\n </p>\r\n <div className=\"mt-1\">\r\n {recentTenants.map(tenant => (\r\n <TenantItem\r\n key={tenant.id}\r\n tenant={tenant}\r\n isSelected={!isGlobalView && currentTenant?.id === tenant.id}\r\n isFavorite={favorites.includes(tenant.id)}\r\n hasThemeOverride={tenantOverrides.includes(tenant.id)}\r\n onSelect={() => handleSelectTenant(tenant.id)}\r\n onToggleFavorite={() => handleToggleFavorite(tenant.id)}\r\n />\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Other Tenants Section */}\r\n {otherTenants.length > 0 && (\r\n <div className={`p-2 ${(favoriteTenants.length > 0 || recentTenants.length > 0) ? 'border-t border-[var(--border-color)]' : ''}`}>\r\n {!search && (\r\n <p className=\"px-2 py-1 text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider flex items-center gap-1.5\">\r\n <Building2 className=\"w-3 h-3\" />\r\n {t('tenants.selector.all', 'Tous')}\r\n </p>\r\n )}\r\n <div className=\"mt-1 max-h-40 overflow-y-auto\">\r\n {otherTenants.map(tenant => (\r\n <TenantItem\r\n key={tenant.id}\r\n tenant={tenant}\r\n isSelected={!isGlobalView && currentTenant?.id === tenant.id}\r\n isFavorite={favorites.includes(tenant.id)}\r\n hasThemeOverride={tenantOverrides.includes(tenant.id)}\r\n onSelect={() => handleSelectTenant(tenant.id)}\r\n onToggleFavorite={() => handleToggleFavorite(tenant.id)}\r\n />\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* No results */}\r\n {search && filteredTenants.length === 0 && (\r\n <div className=\"p-4 text-center text-sm text-[var(--text-tertiary)]\">\r\n {t('tenants.selector.noResults', 'Aucun résultat')}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Footer - Manage Link */}\r\n <div className=\"p-2 border-t border-[var(--border-color)]\">\r\n <Link\r\n to=\"/myspace/tenants\"\r\n className=\"flex items-center justify-center gap-2 px-3 py-2 text-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n onClick={() => setIsOpen(false)}\r\n >\r\n <Eye className=\"w-4 h-4\" />\r\n {t('tenants.selector.viewAll', 'Voir tous mes espaces')} ({userTenants.length})\r\n </Link>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","export type TenantBadgeVariant = 'global' | 'current' | 'other';\r\n\r\n/**\r\n * Determines the badge variant for a notification based on its tenant context.\r\n * - 'global': system-wide notification (no tenant)\r\n * - 'current': notification belongs to the currently active tenant\r\n * - 'other': notification belongs to a different tenant\r\n */\r\nexport function getTenantBadgeVariant(\r\n notificationTenantId: string | null,\r\n currentTenantId: string | null\r\n): TenantBadgeVariant {\r\n if (!notificationTenantId) return 'global';\r\n if (currentTenantId === notificationTenantId) return 'current';\r\n return 'other';\r\n}\r\n","import { api } from './apiClient';\r\nimport type { PaginatedResult } from '../../types/pagination';\r\n\r\n// Dashboard Types\r\nexport interface DashboardOverviewDto {\r\n totalTickets: number;\r\n newTickets: number;\r\n openTickets: number;\r\n resolvedTickets: number;\r\n slaCompliancePercentage: number;\r\n averageSatisfaction: number;\r\n}\r\n\r\nexport interface StatusCountDto {\r\n status: string;\r\n count: number;\r\n}\r\n\r\nexport interface PriorityCountDto {\r\n priority: string;\r\n count: number;\r\n}\r\n\r\nexport interface TypeCountDto {\r\n type: string;\r\n count: number;\r\n}\r\n\r\nexport interface AgentWorkloadDto {\r\n userId: string;\r\n name: string;\r\n email: string;\r\n totalAssigned: number;\r\n openCount: number;\r\n inProgressCount: number;\r\n onHoldCount: number;\r\n}\r\n\r\nexport interface TrendDataDto {\r\n date: string;\r\n created: number;\r\n resolved: number;\r\n}\r\n\r\nexport interface SatisfactionStatsDto {\r\n totalResponses: number;\r\n averageRating: number;\r\n npsScore: number;\r\n ratingDistribution: Record<number, number>;\r\n}\r\n\r\nexport interface SlaComplianceDto {\r\n totalTickets: number;\r\n ticketsWithinSla: number;\r\n ticketsBreached: number;\r\n compliancePercentage: number;\r\n averageResponseMinutes: number;\r\n averageResolutionMinutes: number;\r\n}\r\n\r\n// SLA Types\r\nexport interface SlaConfigurationDto {\r\n id: string;\r\n priority: string;\r\n responseTimeMinutes: number;\r\n resolutionTimeMinutes: number;\r\n isActive: boolean;\r\n responseTimeFormatted: string;\r\n resolutionTimeFormatted: string;\r\n}\r\n\r\nexport interface UpdateSlaConfigurationRequest {\r\n responseTimeMinutes: number;\r\n resolutionTimeMinutes: number;\r\n}\r\n\r\nexport interface EscalationRuleDto {\r\n id: string;\r\n level: string;\r\n triggerType: string;\r\n notifyRoleId: string | null;\r\n delayAfterBreachMinutes: number;\r\n isActive: boolean;\r\n isSystemDefault: boolean;\r\n}\r\n\r\nexport interface TicketSlaDto {\r\n id: string;\r\n ticketId: string;\r\n ticketNumber: string;\r\n ticketTitle: string;\r\n responseDueAt: string;\r\n resolutionDueAt: string;\r\n responseBreached: boolean;\r\n resolutionBreached: boolean;\r\n responseBreachedAt: string | null;\r\n resolutionBreachedAt: string | null;\r\n firstResponseAt: string | null;\r\n escalationLevel: string;\r\n isPaused: boolean;\r\n responseSlaPercentage: number;\r\n resolutionSlaPercentage: number;\r\n}\r\n\r\nexport interface SlaMetricsDto {\r\n totalTickets: number;\r\n ticketsWithinSla: number;\r\n ticketsBreached: number;\r\n slaCompliancePercentage: number;\r\n averageResponseTimeMinutes: number;\r\n averageResolutionTimeMinutes: number;\r\n breachesByPriority: Record<string, number>;\r\n ticketsByEscalationLevel: Record<string, number>;\r\n}\r\n\r\n// Notification Types\r\nexport interface NotificationDto {\r\n id: string;\r\n type: string;\r\n title: string;\r\n message: string;\r\n relatedEntityType: string | null;\r\n relatedEntityId: string | null;\r\n actionUrl: string | null;\r\n isRead: boolean;\r\n readAt: string | null;\r\n createdAt: string;\r\n tenantId: string | null;\r\n tenantName: string | null;\r\n tenantSlug: string | null;\r\n}\r\n\r\n\r\nexport interface NotificationPreferenceDto {\r\n notificationType: string;\r\n emailEnabled: boolean;\r\n inAppEnabled: boolean;\r\n pushEnabled: boolean;\r\n}\r\n\r\nexport interface UpdatePreferencesRequest {\r\n notificationType: string;\r\n emailEnabled: boolean;\r\n inAppEnabled: boolean;\r\n pushEnabled?: boolean;\r\n}\r\n\r\n// Template Types\r\nexport interface ResponseTemplateDto {\r\n id: string;\r\n title: string;\r\n content: string;\r\n category: string;\r\n language: string;\r\n isActive: boolean;\r\n displayOrder: number;\r\n createdAt: string;\r\n updatedAt: string | null;\r\n}\r\n\r\nexport interface CreateTemplateRequest {\r\n title: string;\r\n content: string;\r\n category: string;\r\n}\r\n\r\nexport interface UpdateTemplateRequest {\r\n title: string;\r\n content: string;\r\n category?: string;\r\n displayOrder?: number;\r\n}\r\n\r\nexport interface RenderTemplateRequest {\r\n variables: Record<string, string>;\r\n}\r\n\r\nexport interface RenderedTemplateDto {\r\n content: string;\r\n}\r\n\r\n// Activity Types\r\nexport interface TicketActivityDto {\r\n id: string;\r\n ticketId: string;\r\n action: string;\r\n fieldChanged: string | null;\r\n oldValue: string | null;\r\n newValue: string | null;\r\n changedByUserId: string | null;\r\n changedByUserName: string | null;\r\n createdAt: string;\r\n}\r\n\r\n// Satisfaction Types\r\nexport interface TicketSatisfactionDto {\r\n id: string;\r\n ticketId: string;\r\n rating: number;\r\n comment: string | null;\r\n submittedByUserId: string;\r\n submittedAt: string;\r\n}\r\n\r\nexport interface SubmitSatisfactionRequest {\r\n rating: number;\r\n comment?: string;\r\n}\r\n\r\n// Dashboard API\r\nexport const dashboardApi = {\r\n getOverview: (days = 30) =>\r\n api.get<DashboardOverviewDto>('/api/support/dashboard/overview', { params: { days } }),\r\n\r\n getByStatus: () =>\r\n api.get<StatusCountDto[]>('/api/support/dashboard/by-status'),\r\n\r\n getByPriority: () =>\r\n api.get<PriorityCountDto[]>('/api/support/dashboard/by-priority'),\r\n\r\n getByType: (days = 30) =>\r\n api.get<TypeCountDto[]>('/api/support/dashboard/by-type', { params: { days } }),\r\n\r\n getAgentWorkload: () =>\r\n api.get<AgentWorkloadDto[]>('/api/support/dashboard/agent-workload'),\r\n\r\n getTrends: (days = 30) =>\r\n api.get<TrendDataDto[]>('/api/support/dashboard/trends', { params: { days } }),\r\n\r\n getSatisfactionStats: (days = 30) =>\r\n api.get<SatisfactionStatsDto>('/api/support/dashboard/satisfaction', { params: { days } }),\r\n\r\n getSlaCompliance: (days = 30) =>\r\n api.get<SlaComplianceDto>('/api/support/dashboard/sla-compliance', { params: { days } }),\r\n};\r\n\r\n// SLA API\r\nexport const slaApi = {\r\n getConfigurations: () =>\r\n api.get<SlaConfigurationDto[]>('/api/support/sla/configurations'),\r\n\r\n updateConfiguration: (priority: string, data: UpdateSlaConfigurationRequest) =>\r\n api.put<SlaConfigurationDto>(`/api/support/sla/configurations/${priority}`, data),\r\n\r\n getEscalationRules: () =>\r\n api.get<EscalationRuleDto[]>('/api/support/sla/escalation-rules'),\r\n\r\n getBreachedTickets: (responseBreached = true, resolutionBreached = true) =>\r\n api.get<TicketSlaDto[]>('/api/support/sla/breached', {\r\n params: { responseBreached, resolutionBreached },\r\n }),\r\n\r\n getTicketsApproachingBreach: (warningMinutes = 60) =>\r\n api.get<TicketSlaDto[]>('/api/support/sla/approaching-breach', {\r\n params: { warningMinutes },\r\n }),\r\n\r\n getMetrics: (fromDate?: string, toDate?: string) =>\r\n api.get<SlaMetricsDto>('/api/support/sla/metrics', {\r\n params: { fromDate, toDate },\r\n }),\r\n\r\n escalateTicket: (ticketId: string, level: string, reason?: string) =>\r\n api.post(`/api/support/sla/tickets/${ticketId}/escalate`, { level, reason }),\r\n};\r\n\r\n// Notifications API\r\nexport const notificationsApi = {\r\n getAll: (page = 1, pageSize = 20, isRead?: boolean, tenantId?: string) =>\r\n api.get<PaginatedResult<NotificationDto>>('/api/support/notifications', {\r\n params: { page, pageSize, isRead, tenantId },\r\n }),\r\n\r\n getUnread: (limit = 10) =>\r\n api.get<NotificationDto[]>('/api/support/notifications/unread', { params: { limit } }),\r\n\r\n getUnreadCount: () =>\r\n api.get<{ count: number }>('/api/support/notifications/unread/count'),\r\n\r\n markAsRead: (id: string) =>\r\n api.patch(`/api/support/notifications/${id}/read`),\r\n\r\n markAllAsRead: () =>\r\n api.patch('/api/support/notifications/read-all'),\r\n\r\n delete: (id: string) =>\r\n api.delete(`/api/support/notifications/${id}`),\r\n\r\n deleteAll: () =>\r\n api.delete('/api/support/notifications/all'),\r\n\r\n getPreferences: () =>\r\n api.get<NotificationPreferenceDto[]>('/api/support/notifications/preferences'),\r\n\r\n updatePreferences: (data: UpdatePreferencesRequest) =>\r\n api.put('/api/support/notifications/preferences', data),\r\n};\r\n\r\n// Templates API\r\nexport const templatesApi = {\r\n getAll: (category?: string, activeOnly = true, language?: string) =>\r\n api.get<ResponseTemplateDto[]>('/api/support/templates', {\r\n params: { category, activeOnly, language },\r\n }),\r\n\r\n getById: (id: string) =>\r\n api.get<ResponseTemplateDto>(`/api/support/templates/${id}`),\r\n\r\n getCategories: () =>\r\n api.get<string[]>('/api/support/templates/categories'),\r\n\r\n getByCategory: (category: string) =>\r\n api.get<ResponseTemplateDto[]>(`/api/support/templates/by-category/${category}`),\r\n\r\n create: (data: CreateTemplateRequest) =>\r\n api.post<ResponseTemplateDto>('/api/support/templates', data),\r\n\r\n update: (id: string, data: UpdateTemplateRequest) =>\r\n api.put<ResponseTemplateDto>(`/api/support/templates/${id}`, data),\r\n\r\n activate: (id: string) =>\r\n api.patch(`/api/support/templates/${id}/activate`),\r\n\r\n deactivate: (id: string) =>\r\n api.patch(`/api/support/templates/${id}/deactivate`),\r\n\r\n delete: (id: string) =>\r\n api.delete(`/api/support/templates/${id}`),\r\n\r\n render: (id: string, variables: Record<string, string>) =>\r\n api.post<RenderedTemplateDto>(`/api/support/templates/${id}/render`, { variables }),\r\n};\r\n","import { useState, useEffect, useCallback } from 'react';\r\nimport { notificationsApi, type NotificationDto } from '@/services/api/supportApi';\r\nimport { useSignalRContext, type NotificationDto as SignalRNotificationDto } from '@/contexts/SignalRContext';\r\nimport { useLicense } from '@/contexts/LicenseContext';\r\n\r\n// Entra sync notification types\r\nconst ENTRA_SYNC_TYPES = {\r\n STARTED: 'EntraSyncStarted',\r\n COMPLETED: 'EntraSyncCompleted',\r\n FAILED: 'EntraSyncFailed',\r\n};\r\n\r\n/** Helper: Update notifications list with new notification, handling sync completion */\r\nfunction updateNotificationsList(\r\n prev: NotificationDto[],\r\n newNotification: NotificationDto,\r\n isSyncCompletion: boolean\r\n): NotificationDto[] {\r\n if (isSyncCompletion) {\r\n const sessionId = newNotification.relatedEntityId;\r\n const filtered = prev.filter(n =>\r\n !(n.type === ENTRA_SYNC_TYPES.STARTED && n.relatedEntityId === sessionId)\r\n );\r\n return [newNotification, ...filtered].slice(0, 10);\r\n }\r\n\r\n const exists = prev.some((n) => n.id === newNotification.id);\r\n if (exists) return prev;\r\n return [newNotification, ...prev].slice(0, 10);\r\n}\r\n\r\n/** Convert SignalR notification to NotificationDto */\r\nfunction toNotificationDto(notification: SignalRNotificationDto): NotificationDto {\r\n return {\r\n id: notification.id,\r\n type: notification.type,\r\n title: notification.title,\r\n message: notification.message,\r\n relatedEntityType: notification.relatedEntityType,\r\n relatedEntityId: notification.relatedEntityId,\r\n actionUrl: notification.actionUrl,\r\n isRead: notification.isRead,\r\n readAt: notification.readAt,\r\n createdAt: notification.createdAt,\r\n tenantId: notification.tenantId,\r\n tenantName: notification.tenantName,\r\n tenantSlug: notification.tenantSlug,\r\n };\r\n}\r\n\r\nexport function useNotifications(): {\r\n notifications: NotificationDto[];\r\n unreadCount: number;\r\n loading: boolean;\r\n isConnected: boolean;\r\n loadNotifications: () => Promise<void>;\r\n markAsRead: (id: string) => Promise<void>;\r\n markAllAsRead: () => Promise<void>;\r\n deleteNotification: (id: string) => Promise<void>;\r\n markAsReadSilent: (id: string) => void;\r\n} {\r\n const [unreadCount, setUnreadCount] = useState(0);\r\n const [notifications, setNotifications] = useState<NotificationDto[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n const { isConnected, onNotification, onUnreadCountUpdate } = useSignalRContext();\r\n const { isFeatureEnabled } = useLicense();\r\n const supportEnabled = isFeatureEnabled('support');\r\n\r\n // Subscribe to SignalR events\r\n useEffect(() => {\r\n const unsubNotification = onNotification((notification: SignalRNotificationDto) => {\r\n const newNotification = toNotificationDto(notification);\r\n const isSyncCompletion = notification.type === ENTRA_SYNC_TYPES.COMPLETED ||\r\n notification.type === ENTRA_SYNC_TYPES.FAILED;\r\n setNotifications((prev) => updateNotificationsList(prev, newNotification, isSyncCompletion));\r\n });\r\n\r\n const unsubCount = onUnreadCountUpdate((count: number) => {\r\n setUnreadCount(count);\r\n });\r\n\r\n return () => {\r\n unsubNotification();\r\n unsubCount();\r\n };\r\n }, [onNotification, onUnreadCountUpdate]);\r\n\r\n // Load initial unread count on mount (only if support feature is licensed)\r\n useEffect(() => {\r\n if (!supportEnabled) return;\r\n let ignore = false;\r\n async function fetchCount() {\r\n try {\r\n const result = await notificationsApi.getUnreadCount();\r\n if (!ignore) setUnreadCount(result.count);\r\n } catch (error) {\r\n if (!ignore) console.error('Failed to load unread count:', error);\r\n }\r\n }\r\n fetchCount();\r\n return () => { ignore = true; };\r\n }, [supportEnabled]);\r\n\r\n const loadNotifications = useCallback(async () => {\r\n if (!supportEnabled) return;\r\n try {\r\n setLoading(true);\r\n const result = await notificationsApi.getUnread(10);\r\n setNotifications(result);\r\n } catch (error) {\r\n console.error('Failed to load notifications:', error);\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [supportEnabled]);\r\n\r\n const markAsRead = useCallback(async (id: string) => {\r\n try {\r\n await notificationsApi.markAsRead(id);\r\n setNotifications((prev) =>\r\n prev.map((n) => (n.id === id ? { ...n, isRead: true } : n))\r\n );\r\n setUnreadCount((prev) => Math.max(0, prev - 1));\r\n } catch (error) {\r\n console.error('Failed to mark as read:', error);\r\n }\r\n }, []);\r\n\r\n const markAllAsRead = useCallback(async () => {\r\n try {\r\n await notificationsApi.markAllAsRead();\r\n setNotifications((prev) => prev.map((n) => ({ ...n, isRead: true })));\r\n setUnreadCount(0);\r\n } catch (error) {\r\n console.error('Failed to mark all as read:', error);\r\n }\r\n }, []);\r\n\r\n const deleteNotification = useCallback(async (id: string) => {\r\n try {\r\n await notificationsApi.delete(id);\r\n const deleted = notifications.find((n) => n.id === id);\r\n setNotifications((prev) => prev.filter((n) => n.id !== id));\r\n if (deleted && !deleted.isRead) {\r\n setUnreadCount((prev) => Math.max(0, prev - 1));\r\n }\r\n } catch (error) {\r\n console.error('Failed to delete notification:', error);\r\n }\r\n }, [notifications]);\r\n\r\n const markAsReadSilent = useCallback((id: string) => {\r\n notificationsApi.markAsRead(id);\r\n setUnreadCount((prev) => Math.max(0, prev - 1));\r\n }, []);\r\n\r\n return {\r\n notifications,\r\n unreadCount,\r\n loading,\r\n isConnected,\r\n loadNotifications,\r\n markAsRead,\r\n markAllAsRead,\r\n deleteNotification,\r\n markAsReadSilent,\r\n };\r\n}\r\n","import { useState, useEffect, useRef } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useNavigate } from 'react-router-dom';\r\nimport { Bell, Check, CheckCheck, Trash2, X, Loader2, Wifi, WifiOff, RefreshCw } from 'lucide-react';\r\nimport type { NotificationDto } from '../../services/api/supportApi';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { getTenantBadgeVariant } from '@/utils/notificationTenant';\r\nimport { useNotifications } from '@/hooks/useNotifications';\r\n\r\n// Entra sync notification types\r\nconst ENTRA_SYNC_TYPES = {\r\n STARTED: 'EntraSyncStarted',\r\n COMPLETED: 'EntraSyncCompleted',\r\n FAILED: 'EntraSyncFailed',\r\n};\r\n\r\nexport function NotificationBell(): ReactElement | null {\r\n const navigate = useNavigate();\r\n const { currentTenant, hasMultipleTenants } = useTenant();\r\n const [isOpen, setIsOpen] = useState(false);\r\n const dropdownRef = useRef<HTMLDivElement>(null);\r\n\r\n const {\r\n notifications, unreadCount, loading, isConnected,\r\n loadNotifications, markAsRead, markAllAsRead, deleteNotification, markAsReadSilent,\r\n } = useNotifications();\r\n\r\n // State for live timer updates\r\n const [, setTimerTick] = useState(0);\r\n\r\n // Live timer for EntraSyncStarted notifications\r\n useEffect(() => {\r\n const hasActiveSync = notifications.some(n => n.type === ENTRA_SYNC_TYPES.STARTED && !n.isRead);\r\n if (!hasActiveSync) return;\r\n\r\n const interval = setInterval(() => {\r\n setTimerTick(t => t + 1);\r\n }, 1000);\r\n\r\n return () => clearInterval(interval);\r\n }, [notifications]);\r\n\r\n // Load notifications when dropdown opens\r\n useEffect(() => {\r\n if (isOpen) loadNotifications();\r\n }, [isOpen, loadNotifications]);\r\n\r\n // Close dropdown when clicking outside\r\n useEffect(() => {\r\n const handleClickOutside = (event: MouseEvent) => {\r\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\r\n setIsOpen(false);\r\n }\r\n };\r\n\r\n document.addEventListener('mousedown', handleClickOutside);\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, []);\r\n\r\n const handleMarkAsRead = (id: string, e: React.MouseEvent) => {\r\n e.stopPropagation();\r\n markAsRead(id);\r\n };\r\n\r\n const handleDelete = (id: string, e: React.MouseEvent) => {\r\n e.stopPropagation();\r\n deleteNotification(id);\r\n };\r\n\r\n const handleNotificationClick = (notification: NotificationDto) => {\r\n if (!notification.isRead) {\r\n markAsReadSilent(notification.id);\r\n }\r\n setIsOpen(false);\r\n if (notification.actionUrl) {\r\n navigate(notification.actionUrl);\r\n }\r\n };\r\n\r\n const getNotificationIcon = (type: string) => {\r\n switch (type) {\r\n case 'TicketCreated':\r\n return '🎫';\r\n case 'TicketAssigned':\r\n return '👤';\r\n case 'TicketStatusChanged':\r\n case 'StatusChanged':\r\n return '🔄';\r\n case 'TicketCommented':\r\n case 'CommentAdded':\r\n return '💬';\r\n case 'TicketResolved':\r\n return '✅';\r\n case 'TicketClosed':\r\n return '🔒';\r\n case 'TicketUpdated':\r\n return '📝';\r\n case 'SlaResponseBreached':\r\n case 'SlaResolutionBreached':\r\n return '⚠️';\r\n case 'SlaWarning':\r\n return '⏰';\r\n case 'TicketEscalated':\r\n return '📈';\r\n case 'SatisfactionRequested':\r\n return '⭐';\r\n case 'GroupMemberAdded':\r\n return '👥';\r\n case 'GroupMemberRemoved':\r\n return '🚪';\r\n case 'RoleAssigned':\r\n return '🎭';\r\n case 'RoleRemoved':\r\n return '🚫';\r\n case 'RolePermissionsChanged':\r\n return '🔑';\r\n case 'AccountUpdated':\r\n return '👤';\r\n case 'SystemAnnouncement':\r\n return '📢';\r\n case ENTRA_SYNC_TYPES.STARTED:\r\n return null; // Use animated icon instead\r\n case ENTRA_SYNC_TYPES.COMPLETED:\r\n return '✅';\r\n case ENTRA_SYNC_TYPES.FAILED:\r\n return '❌';\r\n default:\r\n return '📌';\r\n }\r\n };\r\n\r\n // Format elapsed time as mm:ss for sync timer\r\n const formatElapsedTime = (startDateStr: string) => {\r\n const start = new Date(startDateStr).getTime();\r\n const now = Date.now();\r\n const diffSeconds = Math.floor((now - start) / 1000);\r\n const minutes = Math.floor(diffSeconds / 60);\r\n const seconds = diffSeconds % 60;\r\n return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;\r\n };\r\n\r\n const formatTime = (dateStr: string) => {\r\n const date = new Date(dateStr);\r\n const now = new Date();\r\n const diff = now.getTime() - date.getTime();\r\n const minutes = Math.floor(diff / 60000);\r\n const hours = Math.floor(minutes / 60);\r\n const days = Math.floor(hours / 24);\r\n\r\n if (minutes < 1) return 'Just now';\r\n if (minutes < 60) return `${minutes}m ago`;\r\n if (hours < 24) return `${hours}h ago`;\r\n if (days < 7) return `${days}d ago`;\r\n return date.toLocaleDateString();\r\n };\r\n\r\n const renderTenantBadge = (notification: NotificationDto) => {\r\n if (!hasMultipleTenants) return null;\r\n\r\n const variant = getTenantBadgeVariant(notification.tenantId, currentTenant?.id ?? null);\r\n\r\n if (variant === 'global') {\r\n return (\r\n <span className=\"text-[10px] px-1.5 py-0.5 rounded bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 border border-purple-200 dark:border-purple-700\">\r\n System\r\n </span>\r\n );\r\n }\r\n\r\n if (variant === 'current') {\r\n return (\r\n <span className=\"text-[10px] px-1.5 py-0.5 rounded bg-[var(--bg-secondary)] text-[var(--text-secondary)] border border-[var(--border-color)] truncate max-w-[120px]\">\r\n {notification.tenantName}\r\n </span>\r\n );\r\n }\r\n\r\n // 'other' tenant - highlighted\r\n return (\r\n <span className=\"text-[10px] px-1.5 py-0.5 rounded bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 border border-blue-200 dark:border-blue-700 font-medium truncate max-w-[120px]\">\r\n {notification.tenantName}\r\n </span>\r\n );\r\n };\r\n\r\n return (\r\n <div className=\"relative\" ref={dropdownRef}>\r\n <button\r\n onClick={() => setIsOpen(!isOpen)}\r\n className=\"relative p-2 rounded-lg hover:bg-[var(--bg-hover)] transition-colors\"\r\n aria-label=\"Notifications\"\r\n >\r\n <Bell className=\"w-5 h-5\" />\r\n {unreadCount > 0 && (\r\n <span className=\"absolute -top-1 -right-1 w-5 h-5 bg-red-500 text-white text-xs font-bold rounded-full flex items-center justify-center\">\r\n {unreadCount > 9 ? '9+' : unreadCount}\r\n </span>\r\n )}\r\n </button>\r\n\r\n {isOpen && (\r\n <div className=\"absolute right-0 mt-2 w-96 bg-[var(--bg-primary)] rounded-lg shadow-xl border border-[var(--border-color)] z-50 overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"flex items-center justify-between p-4 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-2\">\r\n <h3 className=\"font-semibold\">Notifications</h3>\r\n {isConnected ? (\r\n <span title=\"Real-time connected\"><Wifi className=\"w-3 h-3 text-green-500\" /></span>\r\n ) : (\r\n <span title=\"Offline mode\"><WifiOff className=\"w-3 h-3 text-gray-400\" /></span>\r\n )}\r\n </div>\r\n <div className=\"flex items-center gap-2\">\r\n {unreadCount > 0 && (\r\n <button\r\n onClick={markAllAsRead}\r\n className=\"text-sm text-[var(--color-primary-600)] hover:underline flex items-center gap-1\"\r\n >\r\n <CheckCheck className=\"w-4 h-4\" />\r\n Mark all read\r\n </button>\r\n )}\r\n <button\r\n onClick={() => setIsOpen(false)}\r\n className=\"p-1 rounded hover:bg-[var(--bg-hover)]\"\r\n >\r\n <X className=\"w-4 h-4\" />\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {/* Notifications List */}\r\n <div className=\"max-h-96 overflow-y-auto\">\r\n {loading && (\r\n <div className=\"flex items-center justify-center py-8\">\r\n <Loader2 className=\"w-6 h-6 animate-spin text-[var(--color-primary-600)]\" />\r\n </div>\r\n )}\r\n {!loading && notifications.length > 0 && (\r\n notifications.map((notification) => {\r\n const isActiveSyncNotification = notification.type === ENTRA_SYNC_TYPES.STARTED && !notification.isRead;\r\n const icon = getNotificationIcon(notification.type);\r\n\r\n return (\r\n <div\r\n role=\"button\"\r\n tabIndex={0}\r\n key={notification.id}\r\n onClick={() => handleNotificationClick(notification)}\r\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleNotificationClick(notification); } }}\r\n className={`w-full text-left p-4 border-b border-[var(--border-color)] cursor-pointer hover:bg-[var(--bg-hover)] transition-colors ${\r\n !notification.isRead ? 'bg-blue-50/50 dark:bg-blue-900/10' : ''\r\n } ${isActiveSyncNotification ? 'bg-[var(--color-accent-50)] dark:bg-[var(--color-accent-900)]/20' : ''}`}\r\n >\r\n <div className=\"flex gap-3\">\r\n <span className=\"text-xl flex-shrink-0\">\r\n {isActiveSyncNotification ? (\r\n <RefreshCw className=\"w-5 h-5 text-[var(--color-accent-600)] animate-spin\" />\r\n ) : (\r\n icon\r\n )}\r\n </span>\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"flex items-start justify-between gap-2\">\r\n <div className=\"flex-1 min-w-0\">\r\n <h4 className={`text-sm ${!notification.isRead ? 'font-semibold' : ''}`}>\r\n {notification.title}\r\n </h4>\r\n {renderTenantBadge(notification)}\r\n </div>\r\n {isActiveSyncNotification ? (\r\n <span className=\"text-xs font-mono font-bold text-[var(--color-accent-600)] flex-shrink-0 bg-[var(--color-accent-100)] px-2 py-0.5 rounded\">\r\n ⏱ {formatElapsedTime(notification.createdAt)}\r\n </span>\r\n ) : (\r\n <span className=\"text-xs text-[var(--text-secondary)] flex-shrink-0\">\r\n {formatTime(notification.createdAt)}\r\n </span>\r\n )}\r\n </div>\r\n <p className=\"text-sm text-[var(--text-secondary)] line-clamp-2 mt-1\">\r\n {notification.message}\r\n </p>\r\n </div>\r\n <div className=\"flex flex-col gap-1 flex-shrink-0\">\r\n {!notification.isRead && !isActiveSyncNotification && (\r\n <button\r\n onClick={(e) => handleMarkAsRead(notification.id, e)}\r\n className=\"p-1 rounded hover:bg-[var(--bg-secondary)]\"\r\n title=\"Mark as read\"\r\n >\r\n <Check className=\"w-4 h-4 text-green-600\" />\r\n </button>\r\n )}\r\n {!isActiveSyncNotification && (\r\n <button\r\n onClick={(e) => handleDelete(notification.id, e)}\r\n className=\"p-1 rounded hover:bg-[var(--bg-secondary)]\"\r\n title=\"Delete\"\r\n >\r\n <Trash2 className=\"w-4 h-4 text-red-500\" />\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n })\r\n )}\r\n {!loading && notifications.length === 0 && (\r\n <div className=\"py-8 text-center text-[var(--text-secondary)]\">\r\n <Bell className=\"w-12 h-12 mx-auto mb-2 opacity-50\" />\r\n <p>No notifications</p>\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Footer */}\r\n <div className=\"p-3 border-t border-[var(--border-color)] text-center\">\r\n <button\r\n onClick={() => {\r\n setIsOpen(false);\r\n navigate('/notifications');\r\n }}\r\n className=\"text-sm text-[var(--color-primary-600)] hover:underline\"\r\n >\r\n View all notifications\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n","import { useState, useRef, useEffect, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { createPortal } from 'react-dom';\r\n\r\nexport type TooltipPosition = 'top' | 'bottom' | 'left' | 'right';\r\nexport type TooltipVariant = 'default' | 'error' | 'warning' | 'success' | 'info';\r\n\r\ninterface TooltipProps {\r\n readonly content: ReactNode;\r\n readonly children: ReactNode;\r\n readonly position?: TooltipPosition;\r\n readonly variant?: TooltipVariant;\r\n readonly delay?: number;\r\n readonly disabled?: boolean;\r\n readonly className?: string;\r\n}\r\n\r\nconst variantStyles: Record<TooltipVariant, { bg: string; text: string; border: string; arrow: string }> = {\r\n default: {\r\n bg: 'bg-[var(--bg-tertiary)]',\r\n text: 'text-[var(--text-primary)]',\r\n border: 'border-[var(--border-color)]',\r\n arrow: 'bg-[var(--bg-tertiary)] border-[var(--border-color)]',\r\n },\r\n error: {\r\n bg: 'bg-[var(--tooltip-error-bg)]',\r\n text: 'text-[var(--error-text)]',\r\n border: 'border-[var(--error-border)]',\r\n arrow: 'bg-[var(--tooltip-error-bg)] border-[var(--error-border)]',\r\n },\r\n warning: {\r\n bg: 'bg-[var(--tooltip-warning-bg)]',\r\n text: 'text-[var(--warning-text)]',\r\n border: 'border-[var(--warning-border)]',\r\n arrow: 'bg-[var(--tooltip-warning-bg)] border-[var(--warning-border)]',\r\n },\r\n success: {\r\n bg: 'bg-[var(--tooltip-success-bg)]',\r\n text: 'text-[var(--success-text)]',\r\n border: 'border-[var(--success-border)]',\r\n arrow: 'bg-[var(--tooltip-success-bg)] border-[var(--success-border)]',\r\n },\r\n info: {\r\n bg: 'bg-[var(--tooltip-info-bg)]',\r\n text: 'text-[var(--info-text)]',\r\n border: 'border-[var(--info-border)]',\r\n arrow: 'bg-[var(--tooltip-info-bg)] border-[var(--info-border)]',\r\n },\r\n};\r\n\r\nexport function Tooltip({\r\n content,\r\n children,\r\n position = 'top',\r\n variant = 'default',\r\n delay = 200,\r\n disabled = false,\r\n className = '',\r\n}: TooltipProps): ReactElement {\r\n const styles = variantStyles[variant];\r\n const [isVisible, setIsVisible] = useState(false);\r\n const [coords, setCoords] = useState({ top: 0, left: 0 });\r\n const triggerRef = useRef<HTMLDivElement>(null);\r\n const tooltipRef = useRef<HTMLDivElement>(null);\r\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n\r\n const calculatePosition = () => {\r\n if (!triggerRef.current || !tooltipRef.current) return;\r\n\r\n const triggerRect = triggerRef.current.getBoundingClientRect();\r\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\r\n const scrollX = window.scrollX;\r\n const scrollY = window.scrollY;\r\n const gap = 8;\r\n\r\n let top = 0;\r\n let left = 0;\r\n\r\n switch (position) {\r\n case 'top':\r\n top = triggerRect.top + scrollY - tooltipRect.height - gap;\r\n left = triggerRect.left + scrollX + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case 'bottom':\r\n top = triggerRect.bottom + scrollY + gap;\r\n left = triggerRect.left + scrollX + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case 'left':\r\n top = triggerRect.top + scrollY + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.left + scrollX - tooltipRect.width - gap;\r\n break;\r\n case 'right':\r\n top = triggerRect.top + scrollY + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.right + scrollX + gap;\r\n break;\r\n }\r\n\r\n // Keep tooltip within viewport\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n const padding = 8;\r\n\r\n // Horizontal bounds\r\n if (left < padding) {\r\n left = padding;\r\n } else if (left + tooltipRect.width > viewportWidth - padding) {\r\n left = viewportWidth - tooltipRect.width - padding;\r\n }\r\n\r\n // Vertical bounds\r\n if (top < padding + scrollY) {\r\n top = padding + scrollY;\r\n } else if (top + tooltipRect.height > viewportHeight + scrollY - padding) {\r\n top = viewportHeight + scrollY - tooltipRect.height - padding;\r\n }\r\n\r\n setCoords({ top, left });\r\n };\r\n\r\n useEffect(() => {\r\n if (isVisible) {\r\n calculatePosition();\r\n window.addEventListener('scroll', calculatePosition, true);\r\n window.addEventListener('resize', calculatePosition);\r\n }\r\n\r\n return () => {\r\n window.removeEventListener('scroll', calculatePosition, true);\r\n window.removeEventListener('resize', calculatePosition);\r\n };\r\n }, [isVisible, position]);\r\n\r\n const showTooltip = () => {\r\n if (disabled || !content) return;\r\n timeoutRef.current = setTimeout(() => {\r\n setIsVisible(true);\r\n }, delay);\r\n };\r\n\r\n const hideTooltip = () => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n timeoutRef.current = null;\r\n }\r\n setIsVisible(false);\r\n };\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n }\r\n };\r\n }, []);\r\n\r\n const getArrowClasses = () => {\r\n const base = `absolute w-2 h-2 ${styles.arrow} transform rotate-45`;\r\n switch (position) {\r\n case 'top':\r\n return `${base} -bottom-1 left-1/2 -translate-x-1/2 border-r border-b`;\r\n case 'bottom':\r\n return `${base} -top-1 left-1/2 -translate-x-1/2 border-l border-t`;\r\n case 'left':\r\n return `${base} -right-1 top-1/2 -translate-y-1/2 border-t border-r`;\r\n case 'right':\r\n return `${base} -left-1 top-1/2 -translate-y-1/2 border-b border-l`;\r\n }\r\n };\r\n\r\n return (\r\n <>\r\n <div\r\n ref={triggerRef}\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n onFocus={showTooltip}\r\n onBlur={hideTooltip}\r\n className=\"inline-flex\"\r\n >\r\n {children}\r\n </div>\r\n {isVisible &&\r\n content &&\r\n createPortal(\r\n <div\r\n ref={tooltipRef}\r\n role=\"tooltip\"\r\n style={{\r\n position: 'absolute',\r\n top: coords.top,\r\n left: coords.left,\r\n zIndex: 9999,\r\n }}\r\n className={`\r\n px-3 py-2 text-sm font-medium\r\n ${styles.bg} ${styles.text}\r\n border ${styles.border}\r\n rounded-[var(--radius-button)]\r\n shadow-lg shadow-black/20\r\n max-w-xs\r\n tooltip-animation\r\n ${className}\r\n `}\r\n >\r\n {content}\r\n <div className={getArrowClasses()} />\r\n </div>,\r\n document.body\r\n )}\r\n </>\r\n );\r\n}\r\n\r\n// Convenience wrapper for disabled button tooltips\r\ninterface DisabledButtonTooltipProps {\r\n readonly children: ReactNode;\r\n readonly message: string | undefined;\r\n readonly disabled: boolean;\r\n readonly position?: TooltipPosition;\r\n}\r\n\r\nexport function DisabledButtonTooltip({\r\n children,\r\n message,\r\n disabled,\r\n position = 'top',\r\n}: DisabledButtonTooltipProps): ReactElement {\r\n if (!disabled || !message) {\r\n return <>{children}</>;\r\n }\r\n\r\n return (\r\n <Tooltip content={message} position={position}>\r\n {children}\r\n </Tooltip>\r\n );\r\n}\r\n","import { domToJpeg } from 'modern-screenshot';\r\nimport { logService } from '../logging/logService';\r\n\r\nexport interface ErrorContext {\r\n url: string;\r\n timestamp: Date;\r\n screenshot?: string;\r\n recentErrors: Array<{\r\n message: string;\r\n stack?: string;\r\n url?: string;\r\n component?: string;\r\n level?: string;\r\n timestamp: Date;\r\n }>;\r\n browserInfo: {\r\n userAgent: string;\r\n language: string;\r\n platform: string;\r\n screenWidth: number;\r\n screenHeight: number;\r\n };\r\n}\r\n\r\nconst STORAGE_KEY = 'support_error_context';\r\n\r\nclass ErrorContextService {\r\n async captureContext(): Promise<ErrorContext> {\r\n const context: ErrorContext = {\r\n url: window.location.href,\r\n timestamp: new Date(),\r\n recentErrors: logService.getRecentErrors(),\r\n browserInfo: {\r\n userAgent: navigator.userAgent,\r\n language: navigator.language,\r\n platform: navigator.platform,\r\n screenWidth: window.screen.width,\r\n screenHeight: window.screen.height,\r\n },\r\n };\r\n\r\n try {\r\n const screenshot = await this.captureScreenshot();\r\n context.screenshot = screenshot;\r\n } catch (error) {\r\n logService.logError(new Error('Failed to capture screenshot'), 'ErrorContextService');\r\n }\r\n\r\n return context;\r\n }\r\n\r\n private async captureScreenshot(): Promise<string | undefined> {\r\n try {\r\n // Use high scale for good quality when zooming\r\n // Scale 1.0 = native resolution, good for zooming\r\n const scale = 1.0;\r\n\r\n // Use modern-screenshot which supports modern CSS (oklab, oklch, etc.)\r\n // Use PNG for better quality (lossless), JPEG quality 0.85 for good balance\r\n const dataUrl = await domToJpeg(document.body, {\r\n scale: scale,\r\n quality: 0.85, // High quality JPEG\r\n backgroundColor: '#1a1a2e',\r\n style: {\r\n // Ensure we capture the full page\r\n transform: 'none',\r\n },\r\n filter: (node) => {\r\n // Filter out iframes and videos that might cause issues\r\n if (node instanceof HTMLElement) {\r\n const tagName = node.tagName?.toUpperCase();\r\n return tagName !== 'IFRAME' && tagName !== 'VIDEO';\r\n }\r\n return true;\r\n },\r\n });\r\n\r\n // If too large for sessionStorage (>4MB), try with lower quality\r\n if (dataUrl.length > 4 * 1024 * 1024) {\r\n return await this.recompressScreenshot(dataUrl, 0.7);\r\n }\r\n\r\n return dataUrl;\r\n } catch (error) {\r\n console.error('[ErrorContextService] Screenshot capture failed:', error);\r\n // Fallback: try with lower scale\r\n try {\r\n const dataUrl = await domToJpeg(document.body, {\r\n scale: 0.75,\r\n quality: 0.8,\r\n backgroundColor: '#1a1a2e',\r\n });\r\n return dataUrl;\r\n } catch (fallbackError) {\r\n console.error('[ErrorContextService] Fallback also failed:', fallbackError);\r\n return undefined;\r\n }\r\n }\r\n }\r\n\r\n private async recompressScreenshot(dataUrl: string, quality: number): Promise<string> {\r\n return new Promise((resolve) => {\r\n const img = new Image();\r\n img.onload = () => {\r\n const canvas = document.createElement('canvas');\r\n canvas.width = img.width;\r\n canvas.height = img.height;\r\n const ctx = canvas.getContext('2d');\r\n if (ctx) {\r\n ctx.drawImage(img, 0, 0);\r\n const compressed = canvas.toDataURL('image/jpeg', quality);\r\n resolve(compressed);\r\n } else {\r\n resolve(dataUrl);\r\n }\r\n };\r\n img.onerror = () => resolve(dataUrl);\r\n img.src = dataUrl;\r\n });\r\n }\r\n\r\n saveContext(context: ErrorContext): void {\r\n try {\r\n const json = JSON.stringify(context);\r\n\r\n try {\r\n sessionStorage.setItem(STORAGE_KEY, json);\r\n } catch (quotaError) {\r\n // If quota exceeded, try saving without screenshot\r\n const contextWithoutScreenshot = { ...context, screenshot: undefined };\r\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(contextWithoutScreenshot));\r\n }\r\n } catch (error) {\r\n console.error('[ErrorContextService] Failed to save context:', error);\r\n }\r\n }\r\n\r\n loadContext(): ErrorContext | null {\r\n try {\r\n const data = sessionStorage.getItem(STORAGE_KEY);\r\n\r\n if (data) {\r\n const context = JSON.parse(data);\r\n context.timestamp = new Date(context.timestamp);\r\n context.recentErrors = context.recentErrors.map((e: { timestamp: string }) => ({\r\n ...e,\r\n timestamp: new Date(e.timestamp),\r\n }));\r\n\r\n // Validate screenshot is a valid data URL\r\n if (context.screenshot && !context.screenshot.startsWith('data:image/')) {\r\n context.screenshot = undefined;\r\n }\r\n\r\n return context;\r\n }\r\n } catch (error) {\r\n console.warn('[ErrorContextService] Failed to load context:', error);\r\n }\r\n return null;\r\n }\r\n\r\n clearContext(): void {\r\n sessionStorage.removeItem(STORAGE_KEY);\r\n }\r\n\r\n formatErrorsForDescription(errors: ErrorContext['recentErrors']): string {\r\n if (errors.length === 0) return '';\r\n\r\n const lines = errors.slice(0, 5).map((error, index) => {\r\n const time = error.timestamp.toLocaleTimeString();\r\n return `${index + 1}. [${time}] ${error.message}${error.component ? ` (${error.component})` : ''}`;\r\n });\r\n\r\n return lines.join('\\n');\r\n }\r\n}\r\n\r\nexport const errorContextService = new ErrorContextService();\r\n","import type { TicketType, TicketPriority } from '../api/ticketApi';\r\n\r\nexport interface DraftAttachment {\r\n name: string;\r\n type: string;\r\n size: number;\r\n data: string; // base64 encoded\r\n}\r\n\r\nexport interface DraftErrorContext {\r\n url: string;\r\n timestamp: string;\r\n recentErrors: Array<{\r\n message: string;\r\n stack?: string;\r\n url?: string;\r\n component?: string;\r\n level?: string;\r\n timestamp: string;\r\n }>;\r\n browserInfo: {\r\n userAgent: string;\r\n language: string;\r\n platform: string;\r\n screenWidth: number;\r\n screenHeight: number;\r\n };\r\n}\r\n\r\nexport interface TicketDraft {\r\n id: string;\r\n type: TicketType | null;\r\n title: string;\r\n description: string;\r\n priority: TicketPriority;\r\n includeScreenshot: boolean;\r\n includeErrorLogs: boolean;\r\n screenshot?: string;\r\n errorContext?: DraftErrorContext;\r\n attachments?: DraftAttachment[];\r\n tenantId?: string;\r\n tenantSlug?: string;\r\n tenantName?: string;\r\n createdAt: Date;\r\n updatedAt: Date;\r\n}\r\n\r\ninterface StoredDraft extends Omit<TicketDraft, 'createdAt' | 'updatedAt'> {\r\n createdAt: string;\r\n updatedAt: string;\r\n}\r\n\r\nconst STORAGE_KEY = 'ticket_drafts';\r\nconst MAX_DRAFTS = 5;\r\nconst EXPIRATION_DAYS = 7;\r\n\r\nclass TicketDraftService {\r\n private getDrafts(): TicketDraft[] {\r\n try {\r\n const data = localStorage.getItem(STORAGE_KEY);\r\n if (!data) return [];\r\n\r\n const drafts: StoredDraft[] = JSON.parse(data);\r\n return drafts.map(d => ({\r\n ...d,\r\n createdAt: new Date(d.createdAt),\r\n updatedAt: new Date(d.updatedAt),\r\n }));\r\n } catch (error) {\r\n return [];\r\n }\r\n }\r\n\r\n private saveDrafts(drafts: TicketDraft[]): boolean {\r\n try {\r\n const storedDrafts: StoredDraft[] = drafts.map(d => ({\r\n ...d,\r\n createdAt: d.createdAt.toISOString(),\r\n updatedAt: d.updatedAt.toISOString(),\r\n }));\r\n const json = JSON.stringify(storedDrafts);\r\n localStorage.setItem(STORAGE_KEY, json);\r\n return true;\r\n } catch (error) {\r\n console.error('[TicketDraftService] Failed to save drafts:', error);\r\n // If quota exceeded, try without large data (screenshots and attachments)\r\n if (error instanceof DOMException && error.name === 'QuotaExceededError') {\r\n try {\r\n const draftsWithoutLargeData: StoredDraft[] = drafts.map(d => ({\r\n ...d,\r\n screenshot: undefined,\r\n attachments: undefined,\r\n createdAt: d.createdAt.toISOString(),\r\n updatedAt: d.updatedAt.toISOString(),\r\n }));\r\n localStorage.setItem(STORAGE_KEY, JSON.stringify(draftsWithoutLargeData));\r\n return true;\r\n } catch (retryError) {\r\n console.error('[TicketDraftService] Still failed without large data:', retryError);\r\n }\r\n }\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Clean up expired drafts (older than 7 days)\r\n */\r\n cleanupExpiredDrafts(): void {\r\n const drafts = this.getDrafts();\r\n const now = new Date();\r\n const expirationMs = EXPIRATION_DAYS * 24 * 60 * 60 * 1000;\r\n\r\n const validDrafts = drafts.filter(draft => {\r\n const age = now.getTime() - draft.updatedAt.getTime();\r\n return age < expirationMs;\r\n });\r\n\r\n if (validDrafts.length !== drafts.length) {\r\n this.saveDrafts(validDrafts);\r\n }\r\n }\r\n\r\n /**\r\n * Get all valid drafts (non-expired), sorted by most recent\r\n */\r\n getAllDrafts(): TicketDraft[] {\r\n this.cleanupExpiredDrafts();\r\n return this.getDrafts().sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());\r\n }\r\n\r\n /**\r\n * Get a specific draft by ID\r\n */\r\n getDraft(id: string): TicketDraft | null {\r\n const drafts = this.getAllDrafts();\r\n return drafts.find(d => d.id === id) || null;\r\n }\r\n\r\n /**\r\n * Create a new draft\r\n */\r\n createDraft(): TicketDraft {\r\n const drafts = this.getAllDrafts();\r\n\r\n // Remove oldest draft if at limit\r\n if (drafts.length >= MAX_DRAFTS) {\r\n const oldestDraft = drafts.at(-1)!;\r\n this.deleteDraft(oldestDraft.id);\r\n }\r\n\r\n const now = new Date();\r\n const draft: TicketDraft = {\r\n id: crypto.randomUUID(),\r\n type: null,\r\n title: '',\r\n description: '',\r\n priority: 'Medium',\r\n includeScreenshot: true,\r\n includeErrorLogs: true,\r\n createdAt: now,\r\n updatedAt: now,\r\n };\r\n\r\n const updatedDrafts = [draft, ...this.getDrafts().slice(0, MAX_DRAFTS - 1)];\r\n this.saveDrafts(updatedDrafts);\r\n\r\n return draft;\r\n }\r\n\r\n /**\r\n * Update an existing draft\r\n * Returns { success: boolean, draft: TicketDraft | null }\r\n */\r\n updateDraft(id: string, updates: Partial<Omit<TicketDraft, 'id' | 'createdAt' | 'updatedAt'>>): { success: boolean; draft: TicketDraft | null } {\r\n const drafts = this.getDrafts();\r\n const index = drafts.findIndex(d => d.id === id);\r\n\r\n if (index === -1) {\r\n return { success: false, draft: null };\r\n }\r\n\r\n const updatedDraft: TicketDraft = {\r\n ...drafts[index],\r\n ...updates,\r\n updatedAt: new Date(),\r\n };\r\n\r\n drafts[index] = updatedDraft;\r\n const success = this.saveDrafts(drafts);\r\n\r\n return { success, draft: success ? updatedDraft : null };\r\n }\r\n\r\n /**\r\n * Delete a draft\r\n */\r\n deleteDraft(id: string): boolean {\r\n const drafts = this.getDrafts();\r\n const filtered = drafts.filter(d => d.id !== id);\r\n\r\n if (filtered.length === drafts.length) {\r\n return false;\r\n }\r\n\r\n this.saveDrafts(filtered);\r\n return true;\r\n }\r\n\r\n /**\r\n * Get draft count\r\n */\r\n getDraftCount(): number {\r\n return this.getAllDrafts().length;\r\n }\r\n\r\n /**\r\n * Check if drafts exist\r\n */\r\n hasDrafts(): boolean {\r\n return this.getDraftCount() > 0;\r\n }\r\n\r\n /**\r\n * Get time remaining before draft expires\r\n */\r\n getTimeUntilExpiration(draft: TicketDraft): { days: number; hours: number } {\r\n const expirationMs = EXPIRATION_DAYS * 24 * 60 * 60 * 1000;\r\n const expiresAt = draft.updatedAt.getTime() + expirationMs;\r\n const remainingMs = Math.max(0, expiresAt - Date.now());\r\n\r\n const days = Math.floor(remainingMs / (24 * 60 * 60 * 1000));\r\n const hours = Math.floor((remainingMs % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));\r\n\r\n return { days, hours };\r\n }\r\n}\r\n\r\nexport const ticketDraftService = new TicketDraftService();\r\n","import { useState, useRef, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useNavigate } from 'react-router-dom';\r\nimport { Headset, Loader2, FileEdit, Plus, Trash2, Clock } from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Tooltip } from '@/components/ui/Tooltip';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { errorContextService } from '../../services/support/errorContextService';\r\nimport { ticketDraftService, type TicketDraft } from '../../services/support/ticketDraftService';\r\n\r\ninterface CreateTicketButtonProps {\r\n readonly variant?: 'icon' | 'button';\r\n readonly className?: string;\r\n}\r\n\r\nexport function CreateTicketButton({ variant = 'icon', className = '' }: CreateTicketButtonProps): ReactElement | null {\r\n const { t } = useTranslation(['common', 'support']);\r\n const navigate = useNavigate();\r\n const { hasPermission } = useAuth();\r\n\r\n const [isCapturing, setIsCapturing] = useState(false);\r\n const [showDraftsMenu, setShowDraftsMenu] = useState(false);\r\n const [drafts, setDrafts] = useState<TicketDraft[]>([]);\r\n const menuRef = useRef<HTMLDivElement>(null);\r\n\r\n // Don't render if user can't create tickets (after all hooks)\r\n const canCreate = hasPermission('support.my-tickets.create');\r\n\r\n // Load drafts when menu opens\r\n useEffect(() => {\r\n if (showDraftsMenu) {\r\n setDrafts(ticketDraftService.getAllDrafts());\r\n }\r\n }, [showDraftsMenu]);\r\n\r\n // Close menu on outside click\r\n useEffect(() => {\r\n const handleClickOutside = (event: MouseEvent) => {\r\n if (menuRef.current && !menuRef.current.contains(event.target as Node)) {\r\n setShowDraftsMenu(false);\r\n }\r\n };\r\n\r\n if (showDraftsMenu) {\r\n document.addEventListener('mousedown', handleClickOutside);\r\n }\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, [showDraftsMenu]);\r\n\r\n const captureAndNavigate = async (draftId?: string) => {\r\n setIsCapturing(true);\r\n setShowDraftsMenu(false);\r\n\r\n // Check if we're resuming a draft that already has a screenshot\r\n const existingDraft = draftId ? ticketDraftService.getDraft(draftId) : null;\r\n const draftHasScreenshot = existingDraft?.screenshot;\r\n\r\n let capturedContext: Awaited<ReturnType<typeof errorContextService.captureContext>> | null = null;\r\n\r\n // Only capture new context if not resuming a draft with an existing screenshot\r\n if (!draftHasScreenshot) {\r\n try {\r\n capturedContext = await errorContextService.captureContext();\r\n // Also save to sessionStorage as backup\r\n errorContextService.saveContext(capturedContext);\r\n } catch (error) {\r\n // Silent fail for context capture\r\n }\r\n } else {\r\n // Clear any existing context to avoid overwriting draft screenshot\r\n errorContextService.clearContext();\r\n }\r\n\r\n setIsCapturing(false);\r\n const url = draftId\r\n ? `/support/my-tickets/create?draft=${draftId}`\r\n : '/support/my-tickets/create';\r\n navigate(url);\r\n };\r\n\r\n const handleClick = async () => {\r\n const existingDrafts = ticketDraftService.getAllDrafts();\r\n\r\n if (existingDrafts.length > 0) {\r\n setDrafts(existingDrafts);\r\n setShowDraftsMenu(true);\r\n } else {\r\n await captureAndNavigate();\r\n }\r\n };\r\n\r\n const handleDeleteDraft = (e: React.MouseEvent, draftId: string) => {\r\n e.stopPropagation();\r\n ticketDraftService.deleteDraft(draftId);\r\n const updatedDrafts = ticketDraftService.getAllDrafts();\r\n setDrafts(updatedDrafts);\r\n if (updatedDrafts.length === 0) {\r\n setShowDraftsMenu(false);\r\n }\r\n };\r\n\r\n const formatDraftTitle = (draft: TicketDraft): string => {\r\n if (draft.title) {\r\n return draft.title.length > 30 ? draft.title.substring(0, 30) + '...' : draft.title;\r\n }\r\n if (draft.type) {\r\n return t(`support:tickets.types.${draft.type}`);\r\n }\r\n return t('common:drafts.untitled');\r\n };\r\n\r\n const formatTimeAgo = (date: Date): string => {\r\n const now = new Date();\r\n const diffMs = now.getTime() - date.getTime();\r\n const diffMins = Math.floor(diffMs / 60000);\r\n const diffHours = Math.floor(diffMs / 3600000);\r\n const diffDays = Math.floor(diffMs / 86400000);\r\n\r\n if (diffMins < 1) return t('common:drafts.justNow');\r\n if (diffMins < 60) return t('common:drafts.minutesAgo', { count: diffMins });\r\n if (diffHours < 24) return t('common:drafts.hoursAgo', { count: diffHours });\r\n return t('common:drafts.daysAgo', { count: diffDays });\r\n };\r\n\r\n if (!canCreate) return null;\r\n\r\n const renderDraftsMenu = () => (\r\n <div\r\n ref={menuRef}\r\n className=\"absolute right-0 top-full mt-2 w-80 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg shadow-xl z-50\"\r\n >\r\n <div className=\"p-3 border-b border-[var(--border-color)]\">\r\n <h3 className=\"font-medium text-sm\">{t('common:drafts.existingDrafts')}</h3>\r\n <p className=\"text-xs text-[var(--text-secondary)] mt-1\">\r\n {t('common:drafts.selectOrCreate')}\r\n </p>\r\n </div>\r\n\r\n <div className=\"max-h-64 overflow-y-auto\">\r\n {drafts.map(draft => {\r\n const expiration = ticketDraftService.getTimeUntilExpiration(draft);\r\n return (\r\n <button\r\n type=\"button\"\r\n key={draft.id}\r\n onClick={() => captureAndNavigate(draft.id)}\r\n className=\"w-full p-3 flex items-start gap-3 hover:bg-[var(--bg-hover)] transition-colors text-left border-b border-[var(--border-color)] last:border-b-0 cursor-pointer\"\r\n >\r\n <FileEdit className=\"w-4 h-4 text-[var(--text-secondary)] mt-0.5 flex-shrink-0\" />\r\n <div className=\"flex-1 min-w-0\">\r\n <p className=\"text-sm font-medium truncate\">{formatDraftTitle(draft)}</p>\r\n <div className=\"flex items-center gap-2 text-xs text-[var(--text-secondary)] mt-1 flex-wrap\">\r\n {draft.tenantName && (\r\n <>\r\n <span className=\"px-1.5 py-0.5 rounded bg-[var(--bg-tertiary)] text-[var(--text-secondary)] truncate max-w-[120px]\">\r\n {draft.tenantName}\r\n </span>\r\n <span className=\"text-[var(--text-tertiary)]\">•</span>\r\n </>\r\n )}\r\n <Clock className=\"w-3 h-3\" />\r\n <span>{formatTimeAgo(draft.updatedAt)}</span>\r\n <span className=\"text-[var(--text-tertiary)]\">•</span>\r\n <span className=\"text-[var(--warning-text)]\">\r\n {expiration.days > 0\r\n ? t('common:drafts.expiresInDays', { count: expiration.days })\r\n : t('common:drafts.expiresInHours', { count: expiration.hours })}\r\n </span>\r\n </div>\r\n </div>\r\n <button\r\n onClick={(e) => handleDeleteDraft(e, draft.id)}\r\n className=\"p-1.5 rounded hover:bg-[var(--error-bg)] text-[var(--text-secondary)] hover:text-[var(--error-text)] transition-colors\"\r\n title={t('common:actions.delete')}\r\n >\r\n <Trash2 className=\"w-4 h-4\" />\r\n </button>\r\n </button>\r\n );\r\n })}\r\n </div>\r\n\r\n <div className=\"p-2 border-t border-[var(--border-color)]\">\r\n <button\r\n onClick={() => captureAndNavigate()}\r\n className=\"w-full flex items-center justify-center gap-2 px-4 py-2 bg-[var(--color-primary-600)] text-white rounded-lg hover:bg-[var(--color-primary-700)] transition-colors text-sm\"\r\n >\r\n <Plus className=\"w-4 h-4\" />\r\n {t('common:drafts.createNew')}\r\n </button>\r\n </div>\r\n </div>\r\n );\r\n\r\n if (variant === 'icon') {\r\n return (\r\n <div className=\"relative flex items-center\">\r\n <Tooltip content={t('common:actions.createTicket')} position=\"bottom\">\r\n <button\r\n onClick={handleClick}\r\n disabled={isCapturing}\r\n className={`p-2 rounded-lg hover:bg-[var(--bg-hover)] transition-colors disabled:opacity-50 ${className}`}\r\n >\r\n {isCapturing ? (\r\n <Loader2 className=\"w-5 h-5 animate-spin\" />\r\n ) : (\r\n <Headset className=\"w-5 h-5\" />\r\n )}\r\n </button>\r\n </Tooltip>\r\n {showDraftsMenu && renderDraftsMenu()}\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"relative\">\r\n <button\r\n onClick={handleClick}\r\n disabled={isCapturing}\r\n className={`flex items-center gap-2 px-4 py-2 bg-[var(--color-primary-600)] text-white rounded-lg hover:bg-[var(--color-primary-700)] transition-colors disabled:opacity-50 ${className}`}\r\n >\r\n {isCapturing ? (\r\n <Loader2 className=\"w-4 h-4 animate-spin\" />\r\n ) : (\r\n <Headset className=\"w-4 h-4\" />\r\n )}\r\n {t('common:actions.createTicket')}\r\n </button>\r\n {showDraftsMenu && renderDraftsMenu()}\r\n </div>\r\n );\r\n}\r\n","import { useTranslation } from 'react-i18next';\r\nimport { Layers, Menu, X, LogOut, Bug } from 'lucide-react';\r\nimport { useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { Link } from 'react-router-dom';\r\nimport { ThemeSwitcher } from '@/components/ui/ThemeSwitcher';\r\nimport { LanguageSwitcher } from '@/components/ui/LanguageSwitcher';\r\nimport { AvatarMenu } from '@/components/ui/AvatarMenu';\r\nimport { AppSwitcher } from '@/components/ui/AppSwitcher';\r\nimport { TenantSelector } from '@/components/ui/TenantSelector';\r\nimport { NotificationBell } from '@/components/notifications/NotificationBell';\r\nimport { CreateTicketButton } from '@/components/tickets/CreateTicketButton';\r\nimport { useTenantUrl } from '@/utils/tenantUrl';\r\nimport { useNavigation } from '@/contexts/NavigationContext';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\n\r\ninterface AppHeaderProps {\r\n readonly onMenuToggle?: () => void;\r\n}\r\n\r\nexport function AppHeader({ onMenuToggle }: AppHeaderProps): ReactElement {\r\n const { t } = useTranslation('navigation');\r\n const [isMenuOpen, setIsMenuOpen] = useState(false);\r\n const buildTenantUrl = useTenantUrl();\r\n const { menu } = useNavigation();\r\n const { logout } = useAuth();\r\n\r\n const allApplications = menu?.applications || [];\r\n\r\n return (\r\n <header className=\"fixed top-0 left-0 right-0 z-50 bg-[var(--bg-primary)]/80 backdrop-blur-lg border-b border-[var(--border-color)]\">\r\n <nav className=\"px-4 sm:px-6 lg:px-8\">\r\n <div className=\"flex items-center justify-between h-16\">\r\n <div className=\"flex items-center gap-4\">\r\n {onMenuToggle && (\r\n <button\r\n onClick={onMenuToggle}\r\n className=\"lg:hidden p-2 hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n >\r\n <Menu className=\"w-5 h-5\" />\r\n </button>\r\n )}\r\n\r\n <Link to={buildTenantUrl('/applications')} className=\"flex items-center gap-3 group\">\r\n <div className=\"relative\">\r\n <div className=\"w-10 h-10 animated-gradient flex items-center justify-center\" style={{ borderRadius: 'var(--radius-card)' }}>\r\n <Layers className=\"w-5 h-5 text-white\" />\r\n </div>\r\n </div>\r\n <span className=\"text-xl font-bold gradient-text\">SmartStack</span>\r\n </Link>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-3\">\r\n <TenantSelector />\r\n <div className=\"h-6 w-px bg-[var(--border-color)]\" />\r\n <AppSwitcher />\r\n <div className=\"h-6 w-px bg-[var(--border-color)]\" />\r\n <LanguageSwitcher />\r\n <ThemeSwitcher />\r\n\r\n <div className=\"hidden sm:flex items-center gap-2 pl-3 border-l border-[var(--border-color)]\">\r\n {import.meta.env.DEV && (\r\n <Link\r\n to={buildTenantUrl('/support/tickets/TestBug')}\r\n className=\"p-2 hover:bg-amber-500/20 text-amber-500 transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n title=\"Test Bug Page (DEV)\"\r\n >\r\n <Bug className=\"w-5 h-5\" />\r\n </Link>\r\n )}\r\n <CreateTicketButton />\r\n <NotificationBell />\r\n <AvatarMenu />\r\n </div>\r\n\r\n <button\r\n onClick={() => setIsMenuOpen(!isMenuOpen)}\r\n className=\"sm:hidden p-2 hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n >\r\n {isMenuOpen ? <X className=\"w-6 h-6\" /> : <Menu className=\"w-6 h-6\" />}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {isMenuOpen && (\r\n <div className=\"sm:hidden py-4 border-t border-[var(--border-color)]\">\r\n <div className=\"flex flex-col gap-2\">\r\n {allApplications.map(app => (\r\n <Link\r\n key={app.id}\r\n to={buildTenantUrl(app.route || '#')}\r\n className=\"px-3 py-2 hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n onClick={() => setIsMenuOpen(false)}\r\n >\r\n {app.label}\r\n </Link>\r\n ))}\r\n <hr className=\"my-2 border-[var(--border-color)]\" />\r\n <button\r\n onClick={() => { setIsMenuOpen(false); logout(); }}\r\n className=\"flex items-center gap-2 px-3 py-2 hover:bg-[var(--bg-tertiary)] transition-colors text-red-500\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <LogOut className=\"w-4 h-4\" />\r\n {t('header.logout')}\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </nav>\r\n </header>\r\n );\r\n}\r\n","import { useState, useEffect, useRef, useCallback } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { createPortal } from 'react-dom';\r\nimport { useLocation, useNavigate, Link } from 'react-router-dom';\r\nimport { useTranslation } from 'react-i18next';\r\nimport {\r\n ChevronDown,\r\n ChevronRight,\r\n ChevronLeft,\r\n Settings,\r\n Box,\r\n X,\r\n Star,\r\n} from 'lucide-react';\r\nimport * as LucideIcons from 'lucide-react';\r\nimport type { LucideIcon } from 'lucide-react';\r\nimport { useNavigation, type ModuleDto, type SectionDto, type ResourceDto } from '@/contexts/NavigationContext';\r\nimport { useFavoriteModules } from '@/hooks/useFavoriteModules';\r\nimport { useFavorites } from '@/contexts/FavoritesContext';\r\nimport { useSidebar } from '@/contexts/SidebarContext';\r\nimport { useTenantUrl } from '@/utils/tenantUrl';\r\n\r\n// Dynamic icon component for favorites\r\nfunction DynamicIcon({ name, className }: { name: string; className?: string }) {\r\n const icons = LucideIcons as unknown as Record<string, LucideIcon>;\r\n const IconComponent = icons[name];\r\n if (!IconComponent) {\r\n return <Star className={className} />;\r\n }\r\n return <IconComponent className={className} />;\r\n}\r\n\r\nfunction getIcon(iconName: string | null): LucideIcon {\r\n if (!iconName) return Box;\r\n const icons = LucideIcons as unknown as Record<string, LucideIcon>;\r\n // Try exact match first (PascalCase from DB)\r\n if (icons[iconName]) return icons[iconName];\r\n // Fallback: case-insensitive search for robustness\r\n const lowerName = iconName.toLowerCase();\r\n const matchingKey = Object.keys(icons).find(key => key.toLowerCase() === lowerName);\r\n return matchingKey ? icons[matchingKey] : Box;\r\n}\r\n\r\ninterface FavoriteItemProps {\r\n readonly module: {\r\n readonly id: string;\r\n readonly label: string;\r\n readonly route: string;\r\n readonly icon: string;\r\n readonly applicationLabel: string;\r\n };\r\n readonly isActive: boolean;\r\n readonly isCollapsed: boolean;\r\n readonly onClose: () => void;\r\n readonly buildUrl: (path: string) => string;\r\n}\r\n\r\nfunction FavoriteItem({ module, isActive, isCollapsed, onClose, buildUrl }: FavoriteItemProps) {\r\n const [isHovered, setIsHovered] = useState(false);\r\n const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });\r\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const itemRef = useRef<HTMLDivElement>(null);\r\n\r\n const handleMouseEnter = () => {\r\n if (!isCollapsed) return;\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n timeoutRef.current = null;\r\n }\r\n if (itemRef.current) {\r\n const rect = itemRef.current.getBoundingClientRect();\r\n setTooltipPosition({ top: rect.top, left: rect.right + 8 });\r\n }\r\n setIsHovered(true);\r\n };\r\n\r\n const handleMouseLeave = () => {\r\n if (!isCollapsed) return;\r\n timeoutRef.current = setTimeout(() => {\r\n setIsHovered(false);\r\n }, 150);\r\n };\r\n\r\n // Cleanup timeout on unmount\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n }\r\n };\r\n }, []);\r\n\r\n return (\r\n <div\r\n ref={itemRef}\r\n className=\"relative\"\r\n onMouseEnter={handleMouseEnter}\r\n onMouseLeave={handleMouseLeave}\r\n >\r\n <Link\r\n to={buildUrl(module.route)}\r\n onClick={onClose}\r\n className={`flex items-center transition-colors ${\r\n isCollapsed ? 'justify-center px-2 py-2' : 'gap-3 px-3 py-2'\r\n } ${\r\n isActive\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n title={isCollapsed ? `${module.label} (${module.applicationLabel})` : undefined}\r\n >\r\n <DynamicIcon name={module.icon} className=\"w-4 h-4 flex-shrink-0\" />\r\n {!isCollapsed && (\r\n <span className=\"text-sm font-medium truncate\">{module.label}</span>\r\n )}\r\n </Link>\r\n {isCollapsed && isHovered && createPortal(\r\n <div\r\n className=\"fixed z-[9999] pointer-events-auto\"\r\n style={{ top: tooltipPosition.top, left: tooltipPosition.left }}\r\n onMouseEnter={handleMouseEnter}\r\n onMouseLeave={handleMouseLeave}\r\n >\r\n <div\r\n className=\"px-3 py-2 bg-[var(--bg-secondary)] border border-[var(--border-color)] shadow-lg whitespace-nowrap\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <span className=\"text-sm font-medium text-[var(--text-primary)]\">{module.label}</span>\r\n </div>\r\n </div>,\r\n document.body\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\ninterface ModuleItemProps {\r\n readonly module: ModuleDto;\r\n readonly isExpanded: boolean;\r\n readonly onToggle: () => void;\r\n readonly onNavigate: (route: string) => void;\r\n readonly currentPath: string;\r\n readonly isCollapsed: boolean;\r\n}\r\n\r\nfunction ModuleItem({ module, isExpanded, onToggle, onNavigate, currentPath, isCollapsed }: ModuleItemProps) {\r\n const [isHovered, setIsHovered] = useState(false);\r\n const [flyoutPosition, setFlyoutPosition] = useState({ top: 0, left: 0 });\r\n const [expandedSections, setExpandedSections] = useState<Set<string>>(new Set());\r\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const itemRef = useRef<HTMLDivElement>(null);\r\n const Icon = getIcon(module.icon);\r\n const isActive = module.route ? currentPath === module.route : currentPath.includes(`/${module.code}`);\r\n const hasSections = (module.sections?.length ?? 0) > 0;\r\n const translatedLabel = module.label;\r\n\r\n // Auto-expand section containing current resource route\r\n useEffect(() => {\r\n if (!module.sections) return;\r\n\r\n const sectionWithActiveResource = module.sections.find(section =>\r\n section.resources?.some(resource =>\r\n resource.route && currentPath.startsWith(resource.route)\r\n )\r\n );\r\n\r\n if (sectionWithActiveResource && !expandedSections.has(sectionWithActiveResource.id)) {\r\n setExpandedSections(prev => new Set([...prev, sectionWithActiveResource.id]));\r\n }\r\n }, [currentPath, module.sections, expandedSections]);\r\n\r\n const toggleSection = (sectionId: string) => {\r\n setExpandedSections(prev => {\r\n const next = new Set(prev);\r\n if (next.has(sectionId)) {\r\n next.delete(sectionId);\r\n } else {\r\n next.add(sectionId);\r\n }\r\n return next;\r\n });\r\n };\r\n\r\n const handleMouseEnter = () => {\r\n if (!isCollapsed) return;\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n timeoutRef.current = null;\r\n }\r\n if (itemRef.current) {\r\n const rect = itemRef.current.getBoundingClientRect();\r\n setFlyoutPosition({ top: rect.top, left: rect.right + 8 });\r\n }\r\n setIsHovered(true);\r\n };\r\n\r\n const handleMouseLeave = () => {\r\n if (!isCollapsed) return;\r\n timeoutRef.current = setTimeout(() => {\r\n setIsHovered(false);\r\n }, 150);\r\n };\r\n\r\n // Cleanup timeout on unmount\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n }\r\n };\r\n }, []);\r\n\r\n const handleClick = () => {\r\n if (hasSections && !isCollapsed) {\r\n onToggle();\r\n } else if (module.route) {\r\n onNavigate(module.route);\r\n }\r\n };\r\n\r\n return (\r\n <div\r\n ref={itemRef}\r\n className=\"relative\"\r\n onMouseEnter={handleMouseEnter}\r\n onMouseLeave={handleMouseLeave}\r\n >\r\n <button\r\n onClick={handleClick}\r\n className={`w-full flex items-center transition-all duration-200 ${\r\n isCollapsed ? 'justify-center px-2 py-2.5' : 'gap-3 px-3 py-2.5'\r\n } ${\r\n isActive\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'hover:bg-[var(--bg-tertiary)] text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n title={isCollapsed && !hasSections ? translatedLabel : undefined}\r\n >\r\n <Icon className=\"w-5 h-5 flex-shrink-0\" />\r\n {!isCollapsed && (\r\n <>\r\n <span className=\"flex-1 text-left font-medium text-sm truncate\">{translatedLabel}</span>\r\n {hasSections && (\r\n isExpanded\r\n ? <ChevronDown className=\"w-4 h-4 text-[var(--text-tertiary)] flex-shrink-0\" />\r\n : <ChevronRight className=\"w-4 h-4 text-[var(--text-tertiary)] flex-shrink-0\" />\r\n )}\r\n </>\r\n )}\r\n </button>\r\n\r\n {/* Flyout submenu for collapsed mode - rendered via portal to avoid overflow issues */}\r\n {isCollapsed && isHovered && createPortal(\r\n <div\r\n className=\"fixed z-[9999] pointer-events-auto\"\r\n style={{ top: flyoutPosition.top, left: flyoutPosition.left }}\r\n onMouseEnter={handleMouseEnter}\r\n onMouseLeave={handleMouseLeave}\r\n >\r\n <div\r\n className=\"min-w-[200px] bg-[var(--bg-secondary)] border border-[var(--border-color)] shadow-lg\"\r\n style={{ borderRadius: 'var(--radius-card)' }}\r\n >\r\n {/* Module header */}\r\n <div className=\"px-3 py-2 border-b border-[var(--border-color)]\">\r\n <span className=\"text-sm font-semibold text-[var(--text-primary)]\">{translatedLabel}</span>\r\n </div>\r\n\r\n {/* Sections list or direct link */}\r\n <div className=\"p-1.5\">\r\n {hasSections ? (\r\n module.sections.map(section => {\r\n const SectionIcon = getIcon(section.icon);\r\n const sectionActive = section.route && currentPath === section.route;\r\n return (\r\n <button\r\n key={section.id}\r\n onClick={() => section.route && onNavigate(section.route)}\r\n className={`w-full flex items-center gap-2.5 px-2.5 py-2 transition-colors ${\r\n sectionActive\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'hover:bg-[var(--bg-tertiary)] text-[var(--text-secondary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <SectionIcon className=\"w-4 h-4 flex-shrink-0\" />\r\n <span className=\"text-sm\">{section.label}</span>\r\n </button>\r\n );\r\n })\r\n ) : (\r\n module.route && (\r\n <button\r\n onClick={() => onNavigate(module.route!)}\r\n className=\"w-full flex items-center gap-2.5 px-2.5 py-2 hover:bg-[var(--bg-tertiary)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <Icon className=\"w-4 h-4 flex-shrink-0\" />\r\n <span className=\"text-sm\">Ouvrir</span>\r\n </button>\r\n )\r\n )}\r\n </div>\r\n </div>\r\n </div>,\r\n document.body\r\n )}\r\n\r\n {/* Expanded sections in normal mode */}\r\n {!isCollapsed && isExpanded && hasSections && (\r\n <div className=\"ml-4 pl-4 border-l border-[var(--border-color)] mt-1 space-y-1\">\r\n {module.sections.map(section => (\r\n <SectionItem\r\n key={section.id}\r\n section={section}\r\n currentPath={currentPath}\r\n onNavigate={onNavigate}\r\n isCollapsed={isCollapsed}\r\n isExpanded={expandedSections.has(section.id)}\r\n onToggle={() => toggleSection(section.id)}\r\n />\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\ninterface SectionItemProps {\r\n readonly section: SectionDto;\r\n readonly currentPath: string;\r\n readonly onNavigate: (route: string) => void;\r\n readonly isCollapsed: boolean;\r\n readonly isExpanded: boolean;\r\n readonly onToggle: () => void;\r\n}\r\n\r\nfunction SectionItem({ section, currentPath, onNavigate, isCollapsed, isExpanded, onToggle }: SectionItemProps) {\r\n const Icon = getIcon(section.icon);\r\n const isActive = section.route && (currentPath === section.route || currentPath.startsWith(section.route + '/'));\r\n const hasResources = (section.resources?.length ?? 0) > 0;\r\n const translatedLabel = section.label;\r\n\r\n const handleClick = (e: React.MouseEvent) => {\r\n e.preventDefault();\r\n // Always navigate to section route if it exists\r\n if (section.route) {\r\n onNavigate(section.route);\r\n }\r\n // Also toggle expansion if section has resources\r\n if (hasResources) {\r\n onToggle();\r\n }\r\n };\r\n\r\n if (isCollapsed) return null;\r\n\r\n return (\r\n <div>\r\n <button\r\n onClick={handleClick}\r\n className={`w-full flex items-center gap-3 px-3 py-2 transition-all duration-200 ${\r\n isActive\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'hover:bg-[var(--bg-tertiary)] text-[var(--text-secondary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <Icon className=\"w-4 h-4 flex-shrink-0\" />\r\n <span className=\"flex-1 text-sm text-left\">{translatedLabel}</span>\r\n {hasResources && (\r\n isExpanded\r\n ? <ChevronDown className=\"w-3.5 h-3.5 text-[var(--text-tertiary)] flex-shrink-0\" />\r\n : <ChevronRight className=\"w-3.5 h-3.5 text-[var(--text-tertiary)] flex-shrink-0\" />\r\n )}\r\n </button>\r\n {/* Resources (Level 5) */}\r\n {isExpanded && hasResources && (\r\n <div className=\"ml-4 pl-3 border-l border-[var(--border-color)] mt-1 space-y-0.5\">\r\n {section.resources.map(resource => (\r\n <ResourceItem\r\n key={resource.id}\r\n resource={resource}\r\n currentPath={currentPath}\r\n onNavigate={onNavigate}\r\n />\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\ninterface ResourceItemProps {\r\n readonly resource: ResourceDto;\r\n readonly currentPath: string;\r\n readonly onNavigate: (route: string) => void;\r\n}\r\n\r\nfunction ResourceItem({ resource, currentPath, onNavigate }: ResourceItemProps) {\r\n const isActive = resource.route && currentPath.startsWith(resource.route);\r\n\r\n const handleClick = (e: React.MouseEvent) => {\r\n e.preventDefault();\r\n if (resource.route) {\r\n onNavigate(resource.route);\r\n }\r\n };\r\n\r\n return (\r\n <button\r\n onClick={handleClick}\r\n className={`w-full flex items-center gap-2.5 px-2.5 py-1.5 transition-all duration-200 ${\r\n isActive\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'hover:bg-[var(--bg-tertiary)] text-[var(--text-tertiary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <div className=\"w-1.5 h-1.5 rounded-full bg-current flex-shrink-0\" />\r\n <span className=\"text-xs text-left\">{resource.label}</span>\r\n </button>\r\n );\r\n}\r\n\r\ninterface ModuleSidebarProps {\r\n readonly isOpen: boolean;\r\n readonly onClose: () => void;\r\n}\r\n\r\nconst findExactMatchModule = (currentApp: any, pathname: string): ModuleDto | undefined => {\r\n if (!currentApp?.modules) return undefined;\r\n return currentApp.modules.find((module: ModuleDto) => {\r\n if (!module.sections?.length) return false;\r\n return module.sections.some(section =>\r\n section.route && (pathname === section.route || pathname.startsWith(section.route + '/'))\r\n );\r\n });\r\n};\r\n\r\nconst matchesModuleRoute = (module: ModuleDto, pathname: string): boolean => {\r\n return !!module.route && pathname.startsWith(module.route) && (module.sections?.length ?? 0) > 0;\r\n};\r\n\r\nconst findActiveModule = (currentApp: any, pathname: string): ModuleDto | undefined => {\r\n if (!currentApp) return undefined;\r\n\r\n const exactMatch = findExactMatchModule(currentApp, pathname);\r\n if (exactMatch) return exactMatch;\r\n\r\n return currentApp.modules.find((module: ModuleDto) => matchesModuleRoute(module, pathname));\r\n};\r\n\r\nexport function ModuleSidebar({ isOpen, onClose }: ModuleSidebarProps): ReactElement | null {\r\n const { t } = useTranslation(['navigation', 'common']);\r\n const location = useLocation();\r\n const navigate = useNavigate();\r\n const { getCurrentApplication, isLoading, currentAppCode, menu } = useNavigation();\r\n const { favorites, loading: favoritesLoading } = useFavoriteModules();\r\n const { expandFavorites, clearExpand } = useFavorites();\r\n const { isCollapsed, toggleCollapsed } = useSidebar();\r\n const buildTenantUrl = useTenantUrl();\r\n const [expandedModules, setExpandedModules] = useState<Set<string>>(new Set());\r\n const [favoritesCollapsed, setFavoritesCollapsed] = useState(false);\r\n\r\n // Auto-expand favorites section when triggered from quick access\r\n useEffect(() => {\r\n if (expandFavorites) {\r\n queueMicrotask(() => {\r\n setFavoritesCollapsed(false);\r\n });\r\n clearExpand();\r\n }\r\n }, [expandFavorites, clearExpand]);\r\n\r\n const currentApp = getCurrentApplication();\r\n\r\n // Resolve favorite route to first section if module has sections (handles legacy favorites)\r\n const resolveFavoriteRoute = useCallback((favorite: { id: string; route: string }): string => {\r\n if (menu) {\r\n for (const app of menu.applications) {\r\n const navModule = app.modules.find(m => m.id === favorite.id);\r\n if (navModule?.sections?.length) {\r\n const firstSection = [...navModule.sections].sort((a, b) => a.displayOrder - b.displayOrder)[0];\r\n if (firstSection.route) return firstSection.route;\r\n }\r\n }\r\n }\r\n return favorite.route;\r\n }, [menu]);\r\n\r\n // Auto-expand module containing the current section when navigating\r\n useEffect(() => {\r\n if (!currentApp) return;\r\n\r\n const moduleToExpand = findActiveModule(currentApp, location.pathname);\r\n\r\n if (moduleToExpand && !expandedModules.has(moduleToExpand.id)) {\r\n queueMicrotask(() => {\r\n setExpandedModules(prev => new Set([...prev, moduleToExpand.id]));\r\n });\r\n }\r\n }, [location.pathname, currentApp, expandedModules]);\r\n\r\n const toggleModule = (moduleId: string) => {\r\n setExpandedModules(prev => {\r\n const next = new Set(prev);\r\n if (next.has(moduleId)) {\r\n next.delete(moduleId);\r\n } else {\r\n next.add(moduleId);\r\n }\r\n return next;\r\n });\r\n };\r\n\r\n const handleNavigate = (route: string) => {\r\n navigate(buildTenantUrl(route));\r\n onClose(); // Close sidebar on mobile after navigation\r\n };\r\n\r\n // Use label from API (already localized based on Accept-Language header)\r\n const appLabel = currentApp?.label ?? '';\r\n\r\n if (!currentApp) {\r\n return null;\r\n }\r\n\r\n return (\r\n <>\r\n {isOpen && (\r\n <button\r\n type=\"button\"\r\n className=\"fixed inset-0 bg-black/50 z-40 lg:hidden\"\r\n onClick={onClose}\r\n aria-label=\"Close sidebar\"\r\n />\r\n )}\r\n\r\n <aside className={`group/sidebar fixed top-16 bottom-0 left-0 bg-[var(--bg-primary)] border-r border-[var(--border-color)] transform transition-all duration-300 z-50 overflow-visible ${\r\n isCollapsed ? 'w-16' : 'w-64'\r\n } ${\r\n isOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'\r\n }`}>\r\n {/* Edge toggle button - subtle line with chevron */}\r\n <button\r\n onClick={toggleCollapsed}\r\n className=\"hidden lg:flex absolute top-1/2 -translate-y-1/2 w-3 h-16 items-center justify-center group/toggle transition-all duration-200 z-[9999]\"\r\n style={{ right: '-12px' }}\r\n title={isCollapsed ? t('sidebar.expand', 'Agrandir le menu') : t('sidebar.collapse', 'Réduire le menu')}\r\n >\r\n {/* Vertical line */}\r\n <div className=\"absolute left-0 w-[3px] h-full bg-[var(--border-color)] group-hover/toggle:bg-[var(--color-accent-500)] transition-colors duration-200 rounded-full\" />\r\n {/* Chevron indicator */}\r\n <div className=\"absolute right-0 w-4 h-4 flex items-center justify-center bg-[var(--bg-secondary)] border border-[var(--border-color)] group-hover/toggle:border-[var(--color-accent-500)] group-hover/toggle:text-[var(--color-accent-500)] text-[var(--text-muted)] rounded-full shadow-sm transition-all duration-200\">\r\n {isCollapsed ? (\r\n <ChevronRight className=\"w-3 h-3\" />\r\n ) : (\r\n <ChevronLeft className=\"w-3 h-3\" />\r\n )}\r\n </div>\r\n </button>\r\n <div className=\"flex flex-col h-full\">\r\n <div className={`${isCollapsed ? 'p-2' : 'p-4'}`}>\r\n <div className=\"flex items-center justify-between group/header\">\r\n {isCollapsed ? (\r\n <div className=\"w-full flex justify-center\">\r\n <div\r\n className=\"w-10 h-10 flex items-center justify-center bg-gradient-to-br from-[var(--color-accent-500)] to-[var(--color-accent-700)] text-white shadow-sm\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n title={appLabel}\r\n >\r\n <DynamicIcon name={currentApp.icon || 'LayoutGrid'} className=\"w-5 h-5\" />\r\n </div>\r\n </div>\r\n ) : (\r\n <div className=\"flex items-center gap-3 flex-1 min-w-0\">\r\n <div\r\n className=\"w-10 h-10 flex-shrink-0 flex items-center justify-center bg-gradient-to-br from-[var(--color-accent-500)] to-[var(--color-accent-700)] text-white shadow-sm\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n >\r\n <DynamicIcon name={currentApp.icon || 'LayoutGrid'} className=\"w-5 h-5\" />\r\n </div>\r\n <div className=\"flex-1 min-w-0\">\r\n <h2 className=\"font-semibold text-[var(--text-primary)] truncate\">\r\n {appLabel}\r\n </h2>\r\n <div className=\"flex items-center gap-1.5 mt-0.5\">\r\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-[10px] font-medium bg-[var(--bg-tertiary)] text-[var(--text-secondary)]\" style={{ borderRadius: '4px' }}>\r\n {currentApp.modules.length} {currentApp.modules.length === 1 ? 'module' : 'modules'}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n )}\r\n <button\r\n onClick={onClose}\r\n className=\"lg:hidden p-2 hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n >\r\n <X className=\"w-5 h-5\" />\r\n </button>\r\n </div>\r\n <div className={`mt-3 h-px bg-gradient-to-r from-transparent via-[var(--border-color)] to-transparent ${isCollapsed ? 'mx-1' : ''}`} />\r\n </div>\r\n\r\n <nav className={`flex-1 overflow-y-auto overflow-x-hidden space-y-1 ${isCollapsed ? 'p-2 pt-3' : 'p-3'}`}>\r\n {/* Favorites Section */}\r\n {!favoritesLoading && favorites.length > 0 && (\r\n <div className=\"mb-4\">\r\n {!isCollapsed && (\r\n <div className=\"flex items-center justify-between px-3 py-2 mb-1\">\r\n <button\r\n onClick={() => setFavoritesCollapsed(!favoritesCollapsed)}\r\n className=\"flex items-center gap-2 text-xs font-semibold text-[var(--text-muted)] uppercase tracking-wider hover:text-[var(--text-primary)] transition-colors\"\r\n >\r\n <div className={`transition-transform duration-200 ${favoritesCollapsed ? '' : 'rotate-90'}`}>\r\n <ChevronRight className=\"w-3.5 h-3.5\" />\r\n </div>\r\n <Star className=\"w-3.5 h-3.5 text-amber-500\" />\r\n {t('common:favorites.quickAccess')}\r\n </button>\r\n <Link\r\n to={buildTenantUrl('/myspace/preferences?tab=favorites')}\r\n onClick={onClose}\r\n className=\"p-1.5 text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n title={t('common:favorites.manage')}\r\n >\r\n <Settings className=\"w-3.5 h-3.5\" />\r\n </Link>\r\n </div>\r\n )}\r\n {(isCollapsed || !favoritesCollapsed) && (\r\n <div className=\"space-y-0.5\">\r\n {favorites.map((module) => {\r\n const resolvedRoute = resolveFavoriteRoute(module);\r\n return (\r\n <FavoriteItem\r\n key={module.id}\r\n module={{ ...module, route: resolvedRoute }}\r\n isActive={location.pathname.startsWith(resolvedRoute)}\r\n isCollapsed={isCollapsed}\r\n onClose={onClose}\r\n buildUrl={buildTenantUrl}\r\n />\r\n );\r\n })}\r\n </div>\r\n )}\r\n {!isCollapsed && (\r\n <div className=\"mt-4 pt-4\">\r\n <div className=\"h-px bg-gradient-to-r from-transparent via-[var(--border-color)] to-transparent mb-3\" />\r\n <div className=\"flex items-center gap-2 px-3\">\r\n <div className=\"w-1 h-3.5 bg-gradient-to-b from-[var(--color-accent-500)] to-[var(--color-accent-300)] rounded-full\" />\r\n <span className=\"text-xs font-semibold text-[var(--text-muted)] uppercase tracking-wider\">\r\n {t('sidebar.navigation', 'Navigation')}\r\n </span>\r\n </div>\r\n </div>\r\n )}\r\n {isCollapsed && (\r\n <div className=\"my-3 mx-1\">\r\n <div className=\"h-px bg-gradient-to-r from-transparent via-[var(--border-color)] to-transparent\" />\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {isLoading && (\r\n <div className=\"flex items-center justify-center py-8\">\r\n <div className=\"w-6 h-6 border-2 border-[var(--color-accent-500)] border-t-transparent rounded-full animate-spin\" />\r\n </div>\r\n )}\r\n {!isLoading && currentApp.modules.length > 0 && (\r\n currentApp.modules.map(module => (\r\n <ModuleItem\r\n key={module.id}\r\n module={module}\r\n isExpanded={expandedModules.has(module.id)}\r\n onToggle={() => toggleModule(module.id)}\r\n onNavigate={handleNavigate}\r\n currentPath={location.pathname}\r\n isCollapsed={isCollapsed}\r\n />\r\n ))\r\n )}\r\n {!isLoading && currentApp.modules.length === 0 && !isCollapsed && (\r\n <p className=\"text-sm text-[var(--text-tertiary)] text-center py-8\">\r\n {t('sidebar.noModules', 'Aucun module disponible')}\r\n </p>\r\n )}\r\n </nav>\r\n\r\n {!isCollapsed && (\r\n <div className=\"p-3\">\r\n <div className=\"mb-3 h-px bg-gradient-to-r from-transparent via-[var(--border-color)] to-transparent\" />\r\n <button\r\n onClick={() => handleNavigate(`/${currentAppCode || 'administration'}`)}\r\n className=\"w-full flex items-center gap-2 px-3 py-2 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] transition-all\"\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <ChevronLeft className=\"w-4 h-4\" />\r\n <span className=\"text-sm\">{t('sidebar.backToApps', 'Retour aux applications')}</span>\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n </aside>\r\n </>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { X, ExternalLink, Maximize2, Minimize2 } from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useDocPanel } from '@/contexts/DocPanelContext';\r\n\r\nconst MAX_SIZE = 60;\r\nconst DEFAULT_SIZE = 33;\r\n\r\nexport function DocPanel(): ReactElement | null {\r\n const { t } = useTranslation('common');\r\n const { isOpen, docUrl, closeDocPanel, panelSize, setPanelSize } = useDocPanel();\r\n\r\n if (!isOpen || !docUrl) return null;\r\n\r\n // Build the full URL with embedded mode parameter\r\n const baseUrl = docUrl.startsWith('http') ? docUrl : window.location.origin + docUrl;\r\n const separator = baseUrl.includes('?') ? '&' : '?';\r\n const fullUrl = `${baseUrl}${separator}embedded=true`;\r\n const isMaximized = panelSize >= MAX_SIZE - 1; // Consider maximized if near max\r\n\r\n const handleToggleSize = () => {\r\n if (isMaximized) {\r\n setPanelSize(DEFAULT_SIZE); // Return to default size\r\n } else {\r\n setPanelSize(MAX_SIZE); // Maximize to 60%\r\n }\r\n };\r\n\r\n return (\r\n <div className=\"h-full flex flex-col bg-[var(--bg-primary)] border-l border-[var(--border-color)]\">\r\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-[var(--border-color)] bg-[var(--bg-secondary)]\">\r\n <h3 className=\"font-semibold text-sm flex items-center gap-2\">\r\n {t('docPanel.title')}\r\n </h3>\r\n <div className=\"flex items-center gap-1\">\r\n <button\r\n onClick={handleToggleSize}\r\n className=\"p-1.5 rounded hover:bg-[var(--bg-hover)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors\"\r\n title={isMaximized ? t('docPanel.minimize') : t('docPanel.maximize')}\r\n >\r\n {isMaximized ? (\r\n <Minimize2 className=\"w-4 h-4\" />\r\n ) : (\r\n <Maximize2 className=\"w-4 h-4\" />\r\n )}\r\n </button>\r\n <a\r\n href={docUrl}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"p-1.5 rounded hover:bg-[var(--bg-hover)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors\"\r\n title={t('docPanel.openInNewTab')}\r\n >\r\n <ExternalLink className=\"w-4 h-4\" />\r\n </a>\r\n <button\r\n onClick={closeDocPanel}\r\n className=\"p-1.5 rounded hover:bg-[var(--bg-hover)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors\"\r\n title={t('docPanel.close')}\r\n >\r\n <X className=\"w-4 h-4\" />\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex-1 overflow-hidden\">\r\n <iframe\r\n src={fullUrl}\r\n title={t('docPanel.title')}\r\n className=\"w-full h-full border-0\"\r\n />\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { type ReactNode, useEffect, useRef, useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { Group, Panel, Separator, type PanelSize, type Layout, useGroupCallbackRef } from 'react-resizable-panels';\r\nimport { useDocPanel } from '@/contexts/DocPanelContext';\r\nimport { DocPanel } from '@/components/ui/DocPanel';\r\n\r\ninterface ResizableMainContentProps {\r\n readonly children: ReactNode;\r\n}\r\n\r\nexport function ResizableMainContent({ children }: ResizableMainContentProps): ReactElement {\r\n const { isOpen, panelSize, setPanelSize } = useDocPanel();\r\n const [groupHandle, setGroupHandle] = useGroupCallbackRef();\r\n const prevPanelSizeRef = useRef(panelSize);\r\n const [isGroupReady, setIsGroupReady] = useState(false);\r\n\r\n // Track when the group handle becomes available\r\n useEffect(() => {\r\n if (groupHandle) {\r\n setIsGroupReady(true);\r\n } else {\r\n setIsGroupReady(false);\r\n }\r\n }, [groupHandle]);\r\n\r\n // Update layout when panelSize changes (e.g., from maximize button)\r\n useEffect(() => {\r\n if (groupHandle && isGroupReady && prevPanelSizeRef.current !== panelSize) {\r\n groupHandle.setLayout({\r\n 'main-content': 100 - panelSize,\r\n 'doc-panel': panelSize,\r\n });\r\n prevPanelSizeRef.current = panelSize;\r\n }\r\n }, [panelSize, groupHandle, isGroupReady]);\r\n\r\n if (!isOpen) {\r\n return <>{children}</>;\r\n }\r\n\r\n const handleResize = (size: PanelSize) => {\r\n prevPanelSizeRef.current = size.asPercentage;\r\n setPanelSize(size.asPercentage);\r\n };\r\n\r\n const defaultLayout: Layout = {\r\n 'main-content': 100 - panelSize,\r\n 'doc-panel': panelSize,\r\n };\r\n\r\n return (\r\n <Group\r\n orientation=\"horizontal\"\r\n className=\"h-full\"\r\n defaultLayout={defaultLayout}\r\n groupRef={setGroupHandle}\r\n >\r\n <Panel\r\n id=\"main-content\"\r\n minSize={40}\r\n maxSize={80}\r\n >\r\n <div className=\"w-full h-full overflow-auto\">\r\n {children}\r\n </div>\r\n </Panel>\r\n\r\n <Separator className=\"w-1 bg-[var(--border-color)] hover:bg-[var(--color-primary-400)] transition-colors cursor-col-resize group/separator relative\">\r\n <div className=\"absolute inset-y-0 -left-1 -right-1 group-hover/separator:bg-[var(--color-primary-100)]/20\" />\r\n </Separator>\r\n\r\n <Panel\r\n id=\"doc-panel\"\r\n minSize={20}\r\n maxSize={60}\r\n onResize={handleResize}\r\n >\r\n <DocPanel />\r\n </Panel>\r\n </Group>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { BookOpen } from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useDocPanel } from '@/contexts/DocPanelContext';\r\n\r\nexport function DocEdgeButton(): ReactElement | null {\r\n const { t } = useTranslation('common');\r\n const { isOpen, openDocPanel } = useDocPanel();\r\n\r\n // Don't show when panel is open - panel has its own close button\r\n if (isOpen) return null;\r\n\r\n return (\r\n <button\r\n onClick={() => openDocPanel()}\r\n className=\"hidden lg:flex fixed right-0 top-1/2 -translate-y-1/2 w-3 h-16 items-center justify-center group/toggle transition-all duration-200 z-[100]\"\r\n title={t('docPanel.open')}\r\n aria-label={t('docPanel.open')}\r\n >\r\n {/* Vertical line */}\r\n <div className=\"absolute right-0 w-[3px] h-full bg-[var(--border-color)] group-hover/toggle:bg-[var(--color-primary-500)] transition-colors duration-200 rounded-full\" />\r\n\r\n {/* Circle with icon */}\r\n <div className=\"absolute left-0 -translate-x-full w-6 h-6 flex items-center justify-center bg-[var(--bg-secondary)] border border-[var(--border-color)] group-hover/toggle:border-[var(--color-primary-500)] group-hover/toggle:text-[var(--color-primary-500)] text-[var(--text-muted)] rounded-full shadow-sm transition-all duration-200\">\r\n <BookOpen className=\"w-3 h-3\" />\r\n </div>\r\n\r\n {/* Tooltip on hover */}\r\n <div className=\"absolute right-6 top-1/2 -translate-y-1/2 px-2 py-1 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-md shadow-lg opacity-0 group-hover/toggle:opacity-100 transition-opacity duration-200 pointer-events-none whitespace-nowrap text-xs font-medium text-[var(--text-primary)]\">\r\n {t('docPanel.title')}\r\n </div>\r\n </button>\r\n );\r\n}\r\n","import { useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Link } from 'react-router-dom';\r\nimport { AlertTriangle, X, Shield } from 'lucide-react';\r\nimport { useLicense } from '@/contexts/LicenseContext';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { useTenantUrl } from '@/utils/tenantUrl';\r\n\r\n/**\r\n * LicenseAlertBanner - Displays a warning banner in the header when:\r\n * - The user is a SuperAdmin or has license.read permission\r\n * - The license expires within 30 days, is in read-only mode, or trial <= 7 days\r\n *\r\n * The banner is dismissable (local state only).\r\n */\r\nexport function LicenseAlertBanner(): ReactElement | null {\r\n const { t } = useTranslation('admin');\r\n const { license, isLoading, isReadOnlyMode } = useLicense();\r\n const { user } = useAuth();\r\n const buildTenantUrl = useTenantUrl();\r\n const [dismissed, setDismissed] = useState(false);\r\n\r\n // Don't render while loading or if dismissed\r\n if (isLoading || !license || dismissed) return null;\r\n\r\n // Check if user is admin (SuperAdmin role or license permission)\r\n const isSuperAdmin = user?.roles?.includes('SuperAdmin') ?? false;\r\n const hasLicensePermission = user?.permissions?.some(p =>\r\n p.startsWith('administration.configuration.license') || p === 'administration.configuration.*' || p === 'administration.*'\r\n ) ?? false;\r\n\r\n if (!isSuperAdmin && !hasLicensePermission) return null;\r\n\r\n // Determine if banner should show\r\n const isTrialExpiringSoon = license.isInTrial && license.trialDaysRemaining <= 7;\r\n const isExpiringSoon = !license.isInTrial && license.daysUntilExpiration <= 30 && license.status === 'Active';\r\n const shouldShow = isReadOnlyMode || isTrialExpiringSoon || isExpiringSoon;\r\n\r\n if (!shouldShow) return null;\r\n\r\n // Determine banner style\r\n const isError = isReadOnlyMode;\r\n const bannerClasses = isError\r\n ? 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800 text-red-800 dark:text-red-200'\r\n : 'bg-amber-50 dark:bg-amber-900/20 border-amber-200 dark:border-amber-800 text-amber-800 dark:text-amber-200';\r\n\r\n const Icon = isError ? Shield : AlertTriangle;\r\n\r\n // Determine message\r\n let message: string;\r\n if (isReadOnlyMode) {\r\n message = t('license.alert.readOnly', 'Your license has expired. The platform is in read-only mode.');\r\n } else if (isTrialExpiringSoon) {\r\n message = t('license.alert.trialExpiring', 'Your trial expires in {{days}} days. Activate a license to continue using the platform.', { days: license.trialDaysRemaining });\r\n } else {\r\n message = t('license.alert.expiringSoon', 'Your license expires in {{days}} days. Renew to avoid service interruption.', { days: license.daysUntilExpiration });\r\n }\r\n\r\n return (\r\n <div className={`border-b ${bannerClasses}`}>\r\n <div className=\"px-4 sm:px-6 lg:px-8 py-2 flex items-center justify-between gap-4\">\r\n <div className=\"flex items-center gap-2 min-w-0\">\r\n <Icon className=\"w-4 h-4 flex-shrink-0\" />\r\n <span className=\"text-sm font-medium truncate\">{message}</span>\r\n </div>\r\n <div className=\"flex items-center gap-2 flex-shrink-0\">\r\n <Link\r\n to={buildTenantUrl('/administration/configuration/license')}\r\n className={`text-xs font-medium px-3 py-1 rounded-md transition-colors ${\r\n isError\r\n ? 'bg-red-100 dark:bg-red-900/40 hover:bg-red-200 dark:hover:bg-red-800/40'\r\n : 'bg-amber-100 dark:bg-amber-900/40 hover:bg-amber-200 dark:hover:bg-amber-800/40'\r\n }`}\r\n >\r\n {t('license.alert.manage', 'Manage')}\r\n </Link>\r\n <button\r\n onClick={() => setDismissed(true)}\r\n className=\"p-1 rounded-md hover:bg-black/10 dark:hover:bg-white/10 transition-colors\"\r\n aria-label={t('common:dismiss', 'Dismiss')}\r\n >\r\n <X className=\"w-3.5 h-3.5\" />\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Link } from 'react-router-dom';\r\nimport { AlertTriangle, Clock, Key, Shield } from 'lucide-react';\r\nimport { useLicense } from '@/contexts/LicenseContext';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { useTenantUrl } from '@/utils/tenantUrl';\r\n\r\n/**\r\n * LicenseContentBanner - Persistent (non-dismissable) banner displayed in the\r\n * content area of all layouts when the license requires attention.\r\n *\r\n * Visible to admins only (SuperAdmin or license permission).\r\n * Covers: trial, no-license, expired, revoked, and read-only states.\r\n */\r\nexport function LicenseContentBanner(): ReactElement | null {\r\n const { t } = useTranslation('admin');\r\n const { license, isLoading, isReadOnlyMode } = useLicense();\r\n const { user } = useAuth();\r\n const buildTenantUrl = useTenantUrl();\r\n\r\n if (isLoading) return null;\r\n\r\n // Check if user is admin (SuperAdmin role or license permission)\r\n const isSuperAdmin = user?.roles?.includes('SuperAdmin') ?? false;\r\n const hasLicensePermission = user?.permissions?.some(p =>\r\n p.startsWith('administration.configuration.license') || p === 'administration.configuration.*' || p === 'administration.*'\r\n ) ?? false;\r\n\r\n if (!isSuperAdmin && !hasLicensePermission) return null;\r\n\r\n // Determine banner variant based on priority\r\n type Variant = 'error' | 'warning' | 'info';\r\n let variant: Variant;\r\n let Icon: typeof Shield;\r\n let message: string;\r\n\r\n if (isReadOnlyMode) {\r\n variant = 'error';\r\n Icon = Shield;\r\n message = t('license.contentBanner.expired', 'Your license has expired. The platform is in restricted mode. Please renew or activate a new license.');\r\n } else if (license?.status === 'Revoked') {\r\n variant = 'error';\r\n Icon = Shield;\r\n message = t('license.contentBanner.revoked', 'Your license has been revoked. Please contact support or activate a new license.');\r\n } else if (license?.status === 'Expired') {\r\n variant = 'error';\r\n Icon = AlertTriangle;\r\n message = t('license.contentBanner.expired', 'Your license has expired. The platform is in restricted mode. Please renew or activate a new license.');\r\n } else if (license?.status === 'NoLicense' || !license) {\r\n variant = 'warning';\r\n Icon = Key;\r\n message = t('license.contentBanner.noLicense', 'No license is activated. Activate a license to use the platform without restrictions.');\r\n } else if (license.isInTrial && license.trialDaysRemaining <= 7) {\r\n variant = 'warning';\r\n Icon = Clock;\r\n message = t('license.contentBanner.trialUrgent', 'Your trial expires in {{days}} days. Activate a license now to avoid losing access.', { days: license.trialDaysRemaining });\r\n } else if (license.isInTrial && license.trialDaysRemaining > 7) {\r\n variant = 'info';\r\n Icon = Clock;\r\n message = t('license.contentBanner.trial', 'You are using a trial license. {{days}} days remaining. Activate a license to unlock the full platform.', { days: license.trialDaysRemaining });\r\n } else {\r\n // Active license, not trial, not read-only — nothing to show\r\n return null;\r\n }\r\n\r\n const variantClasses: Record<Variant, string> = {\r\n error: 'bg-[var(--error-bg)] text-[var(--error-text)] border-[var(--error-border)]',\r\n warning: 'bg-[var(--warning-bg)] text-[var(--warning-text)] border-[var(--warning-border)]',\r\n info: 'bg-[var(--info-bg)] text-[var(--info-text)] border-[var(--info-border)]',\r\n };\r\n\r\n const ctaClasses: Record<Variant, string> = {\r\n error: 'bg-[var(--error-border)] hover:opacity-80 text-white',\r\n warning: 'bg-[var(--warning-border)] hover:opacity-80 text-white',\r\n info: 'bg-[var(--info-border)] hover:opacity-80 text-white',\r\n };\r\n\r\n return (\r\n <div className={`mb-4 rounded-lg border p-4 ${variantClasses[variant]}`} data-testid=\"license-content-banner\">\r\n <div className=\"flex items-center justify-between gap-4\">\r\n <div className=\"flex items-center gap-3 min-w-0\">\r\n <Icon className=\"w-5 h-5 flex-shrink-0\" />\r\n <span className=\"text-sm font-medium\">{message}</span>\r\n </div>\r\n <Link\r\n to={buildTenantUrl('/administration/configuration/license')}\r\n className={`text-xs font-medium px-4 py-1.5 rounded-md transition-opacity flex-shrink-0 ${ctaClasses[variant]}`}\r\n >\r\n {t('license.contentBanner.activate', 'Activate license')}\r\n </Link>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { Outlet } from 'react-router-dom';\r\nimport { AppHeader } from '@/components/layout/AppHeader';\r\nimport { ModuleSidebar } from '@/components/layout/ModuleSidebar';\r\nimport { ResizableMainContent } from '@/components/layout/ResizableMainContent';\r\nimport { DocEdgeButton } from '@/components/ui/DocEdgeButton';\r\nimport { useNavigation } from '@/contexts/NavigationContext';\r\nimport { DocPanelProvider } from '@/contexts/DocPanelContext';\r\nimport { useSidebar } from '@/contexts/SidebarContext';\r\nimport { LicenseAlertBanner } from '@/components/license/LicenseAlertBanner';\r\nimport { LicenseContentBanner } from '@/components/license/LicenseContentBanner';\r\n\r\nfunction AppLayoutContent() {\r\n const [sidebarOpen, setSidebarOpen] = useState(false);\r\n const { currentAppCode } = useNavigation();\r\n const { isCollapsed } = useSidebar();\r\n\r\n const showSidebar = !!currentAppCode;\r\n\r\n const getSidebarPadding = () => {\r\n if (!showSidebar) return '';\r\n return isCollapsed ? 'lg:pl-16' : 'lg:pl-64';\r\n };\r\n\r\n return (\r\n <div className=\"min-h-screen bg-[var(--bg-primary)]\">\r\n <AppHeader onMenuToggle={() => setSidebarOpen(!sidebarOpen)} />\r\n {showSidebar && (\r\n <ModuleSidebar isOpen={sidebarOpen} onClose={() => setSidebarOpen(false)} />\r\n )}\r\n <main\r\n className={`pt-16 transition-all duration-300 h-[calc(100vh-4rem)] ${getSidebarPadding()}`}\r\n >\r\n <LicenseAlertBanner />\r\n <ResizableMainContent>\r\n <div className=\"p-4 sm:p-6 lg:px-10 h-full overflow-auto\">\r\n <LicenseContentBanner />\r\n <Outlet />\r\n </div>\r\n </ResizableMainContent>\r\n </main>\r\n <DocEdgeButton />\r\n </div>\r\n );\r\n}\r\n\r\nexport function AppLayout(): ReactElement {\r\n return (\r\n <DocPanelProvider>\r\n <AppLayoutContent />\r\n </DocPanelProvider>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { Outlet } from 'react-router-dom';\r\nimport { Layers } from 'lucide-react';\r\n\r\nexport function AuthLayout(): ReactElement {\r\n return (\r\n <div className=\"min-h-screen bg-[var(--bg-primary)] flex items-center justify-center p-4\">\r\n <div className=\"w-full max-w-md\">\r\n <div className=\"text-center mb-8\">\r\n <a href=\"/\" className=\"inline-flex items-center gap-3\">\r\n <div className=\"w-12 h-12 rounded-xl animated-gradient flex items-center justify-center\">\r\n <Layers className=\"w-6 h-6 text-white\" />\r\n </div>\r\n <span className=\"text-2xl font-bold gradient-text\">SmartStack</span>\r\n </a>\r\n </div>\r\n <div className=\"bg-[var(--bg-secondary)] rounded-2xl border border-[var(--border-color)] p-8\">\r\n <Outlet />\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useTranslation } from 'react-i18next';\r\nimport { Layers, Menu, X, Bug } from 'lucide-react';\r\nimport { useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { Link } from 'react-router-dom';\r\nimport { ThemeSwitcher } from '@/components/ui/ThemeSwitcher';\r\nimport { LanguageSwitcher } from '@/components/ui/LanguageSwitcher';\r\nimport { AvatarMenu } from '@/components/ui/AvatarMenu';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\n\r\nexport function Header(): ReactElement {\r\n const { t } = useTranslation('navigation');\r\n const { isAuthenticated } = useAuth();\r\n const [isMenuOpen, setIsMenuOpen] = useState(false);\r\n\r\n return (\r\n <header className=\"fixed top-0 left-0 right-0 z-50 bg-[var(--bg-primary)]/80 backdrop-blur-lg border-b border-[var(--border-color)]\">\r\n <nav className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\r\n <div className=\"flex items-center justify-between h-16\">\r\n <a href=\"/\" className=\"flex items-center gap-3 group\">\r\n <div className=\"relative\">\r\n <div className=\"w-10 h-10 rounded-xl animated-gradient flex items-center justify-center\">\r\n <Layers className=\"w-5 h-5 text-white\" />\r\n </div>\r\n </div>\r\n <span className=\"text-xl font-bold gradient-text\">SmartStack</span>\r\n </a>\r\n\r\n <div className=\"flex items-center gap-4\">\r\n {import.meta.env.DEV && (\r\n <Link\r\n to=\"/support/tickets/TestBug\"\r\n className=\"p-2 hover:bg-amber-500/20 text-amber-500 transition-colors rounded-lg\"\r\n title=\"Test Bug Page (DEV)\"\r\n >\r\n <Bug className=\"w-5 h-5\" />\r\n </Link>\r\n )}\r\n <LanguageSwitcher />\r\n <ThemeSwitcher />\r\n {isAuthenticated ? (\r\n <AvatarMenu />\r\n ) : (\r\n <a\r\n href=\"/login\"\r\n className=\"hidden md:inline-flex px-4 py-2 rounded-xl bg-primary-600 hover:bg-primary-700 text-white font-medium transition-colors\"\r\n >\r\n {t('header.login')}\r\n </a>\r\n )}\r\n\r\n <button\r\n onClick={() => setIsMenuOpen(!isMenuOpen)}\r\n className=\"md:hidden p-2 rounded-lg hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n >\r\n {isMenuOpen ? <X className=\"w-6 h-6\" /> : <Menu className=\"w-6 h-6\" />}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {isMenuOpen && (\r\n <div className=\"md:hidden py-4 border-t border-[var(--border-color)]\">\r\n <div className=\"flex flex-col gap-4\">\r\n {!isAuthenticated && (\r\n <a\r\n href=\"/login\"\r\n className=\"px-4 py-2 rounded-xl bg-primary-600 hover:bg-primary-700 text-white font-medium transition-colors text-center\"\r\n >\r\n {t('header.login')}\r\n </a>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n </nav>\r\n </header>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { Layers, Github, Twitter, Linkedin } from 'lucide-react';\r\n\r\nexport function Footer(): ReactElement {\r\n const currentYear = new Date().getFullYear();\r\n\r\n return (\r\n <footer className=\"bg-[var(--bg-secondary)] border-t border-[var(--border-color)]\">\r\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12\">\r\n <div className=\"grid grid-cols-1 md:grid-cols-4 gap-8\">\r\n {/* Brand */}\r\n <div className=\"md:col-span-1\">\r\n <a href=\"/\" className=\"flex items-center gap-3 mb-4\">\r\n <div className=\"w-10 h-10 rounded-xl animated-gradient flex items-center justify-center\">\r\n <Layers className=\"w-5 h-5 text-white\" />\r\n </div>\r\n <span className=\"text-xl font-bold gradient-text\">SmartStack</span>\r\n </a>\r\n <p className=\"text-[var(--text-tertiary)] text-sm\">\r\n Le socle moderne pour vos applications d'entreprise.\r\n </p>\r\n </div>\r\n\r\n {/* Product */}\r\n <div>\r\n <h4 className=\"font-semibold text-[var(--text-primary)] mb-4\">Produit</h4>\r\n <ul className=\"space-y-2\">\r\n <li>\r\n <a href=\"#features\" className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] text-sm transition-colors\">\r\n Fonctionnalités\r\n </a>\r\n </li>\r\n <li>\r\n <a href=\"#modules\" className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] text-sm transition-colors\">\r\n Modules\r\n </a>\r\n </li>\r\n <li>\r\n <a href=\"#pricing\" className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] text-sm transition-colors\">\r\n Tarifs\r\n </a>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n {/* Company */}\r\n <div>\r\n <h4 className=\"font-semibold text-[var(--text-primary)] mb-4\">Entreprise</h4>\r\n <ul className=\"space-y-2\">\r\n <li>\r\n <a href=\"#about\" className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] text-sm transition-colors\">\r\n À propos\r\n </a>\r\n </li>\r\n <li>\r\n <a href=\"#contact\" className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] text-sm transition-colors\">\r\n Contact\r\n </a>\r\n </li>\r\n <li>\r\n <a href=\"#careers\" className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] text-sm transition-colors\">\r\n Carrières\r\n </a>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n {/* Legal */}\r\n <div>\r\n <h4 className=\"font-semibold text-[var(--text-primary)] mb-4\">Légal</h4>\r\n <ul className=\"space-y-2\">\r\n <li>\r\n <a href=\"#privacy\" className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] text-sm transition-colors\">\r\n Confidentialité\r\n </a>\r\n </li>\r\n <li>\r\n <a href=\"#terms\" className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] text-sm transition-colors\">\r\n Conditions\r\n </a>\r\n </li>\r\n <li>\r\n <a href=\"#security\" className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] text-sm transition-colors\">\r\n Sécurité\r\n </a>\r\n </li>\r\n </ul>\r\n </div>\r\n </div>\r\n\r\n {/* Bottom */}\r\n <div className=\"mt-12 pt-8 border-t border-[var(--border-color)] flex flex-col sm:flex-row items-center justify-between gap-4\">\r\n <p className=\"text-[var(--text-tertiary)] text-sm\">\r\n © {currentYear} SmartStack. Tous droits réservés.\r\n </p>\r\n <div className=\"flex items-center gap-4\">\r\n <a\r\n href=\"https://github.com\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] transition-colors\"\r\n >\r\n <Github className=\"w-5 h-5\" />\r\n </a>\r\n <a\r\n href=\"https://twitter.com\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] transition-colors\"\r\n >\r\n <Twitter className=\"w-5 h-5\" />\r\n </a>\r\n <a\r\n href=\"https://linkedin.com\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"text-[var(--text-tertiary)] hover:text-[var(--text-primary)] transition-colors\"\r\n >\r\n <Linkedin className=\"w-5 h-5\" />\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n </footer>\r\n );\r\n}\r\n","import { useState, useMemo, useRef, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useNavigate } from 'react-router-dom';\r\nimport { Search, X, FileText, ArrowRight, Layers, Database, Download, Server, Building2, Link2, Bot, Headphones, Settings, Presentation, Monitor } from 'lucide-react';\r\n\r\ninterface DocPage {\r\n title: string;\r\n description: string;\r\n href: string;\r\n category: 'developer' | 'user';\r\n tags: string[];\r\n icon: React.ElementType;\r\n}\r\n\r\n// Index de toutes les pages de documentation\r\nconst docsIndex: DocPage[] = [\r\n // Developer pages\r\n {\r\n title: 'Infrastructure',\r\n description: 'Architecture technique, structure du projet et technologies utilisees',\r\n href: '/system/docs/developer/infrastructure',\r\n category: 'developer',\r\n tags: ['.NET 10', 'React 19', 'EF Core', 'SQL Server', 'architecture'],\r\n icon: Server,\r\n },\r\n {\r\n title: 'Installation',\r\n description: 'Guide complet pour installer et configurer l\\'environnement de developpement',\r\n href: '/system/docs/developer/installation',\r\n category: 'developer',\r\n tags: ['setup', 'configuration', 'prerequisites', 'npm', 'dotnet', 'CLI', 'MCP'],\r\n icon: Download,\r\n },\r\n {\r\n title: 'Base de donnees',\r\n description: 'Modele conceptuel de donnees avec schemas, tables et relations',\r\n href: '/system/docs/developer/database',\r\n category: 'developer',\r\n tags: [\r\n // General\r\n 'MCD', 'Schemas', 'Relations', 'SQL', 'EF Core', 'migrations', 'ERD', 'diagramme',\r\n // Schema names\r\n 'nav', 'auth', 'usr', 'support', 'ten', 'ai', 'wkf', 'cfg', 'entra', 'ref', 'loc', 'lic',\r\n // Navigation tables\r\n 'Contexts', 'Applications', 'Modules', 'Sections', 'Resources', 'ContextMaintenance',\r\n // Auth tables\r\n 'Users', 'Roles', 'UserRoles', 'Permissions', 'RolePermissions', 'RefreshTokens', 'Sessions',\r\n // User tables\r\n 'Profiles', 'Preferences',\r\n // Support tables\r\n 'Tickets', 'TicketComments', 'TicketAttachments', 'TicketTemplates', 'Notifications',\r\n // Tenant tables\r\n 'Tenants', 'TenantApplications', 'TenantUsers',\r\n // AI tables\r\n 'Providers', 'Models', 'Prompts', 'PromptVersions', 'PromptExecutions',\r\n // Workflow tables\r\n 'Workflows', 'WorkflowSteps', 'WorkflowExecutions', 'EmailTemplates',\r\n // Config tables\r\n 'Settings', 'SettingCategories',\r\n // Entra tables\r\n 'Groups', 'GroupMembers', 'SyncStates',\r\n // Reference tables\r\n 'Companies', 'Departments', 'Positions',\r\n // Localization tables\r\n 'Languages', 'Translations',\r\n // License tables\r\n 'Licenses', 'LicenseFeatures',\r\n // Key columns\r\n 'Status', 'Type', 'Priority', 'TenantId', 'UserId', 'RoleId', 'PermissionId',\r\n 'CreatedAt', 'UpdatedAt', 'CreatedBy', 'UpdatedBy', 'IsDeleted', 'DeletedAt',\r\n // Enum values\r\n 'TenantStatus', 'TicketStatus', 'TicketPriority', 'TicketType', 'UserType', 'Theme',\r\n 'Pending', 'Active', 'Suspended', 'Archived', 'Open', 'InProgress', 'Resolved', 'Closed',\r\n 'Low', 'Medium', 'High', 'Critical', 'Bug', 'FeatureRequest', 'Support', 'Question',\r\n ],\r\n icon: Database,\r\n },\r\n {\r\n title: 'Architecture Dual-DbContext',\r\n description: 'Separation Core/Extensions pour la distribution NuGet et les projets client',\r\n href: '/system/docs/developer/dual-dbcontext',\r\n category: 'developer',\r\n tags: ['CoreDbContext', 'ExtensionsDbContext', 'NuGet', 'Migrations', 'schema'],\r\n icon: Layers,\r\n },\r\n {\r\n title: 'FK Cross-Schema',\r\n description: 'Comment referencer les entites Core depuis vos entites Extensions',\r\n href: '/system/docs/developer/cross-schema-fk',\r\n category: 'developer',\r\n tags: ['ICoreDataService', 'GUID FK', 'No Navigation', 'foreign key'],\r\n icon: Link2,\r\n },\r\n {\r\n title: 'Mode Multi-Tenant',\r\n description: 'Configuration Single-Tenant vs Multi-Tenant et migration entre les modes',\r\n href: '/system/docs/developer/multi-tenant-mode',\r\n category: 'developer',\r\n tags: ['Single-Tenant', 'Multi-Tenant', 'Configuration', 'tenant', 'workspace'],\r\n icon: Building2,\r\n },\r\n {\r\n title: 'Integration Microsoft Teams',\r\n description: 'Embarquer SmartStack dans Teams via Website Tab sans developpement',\r\n href: '/system/docs/developer/teams-integration',\r\n category: 'developer',\r\n tags: ['Microsoft Teams', 'Teams', 'Website Tab', 'iframe', 'SSO', 'embedding'],\r\n icon: Monitor,\r\n },\r\n // User pages\r\n {\r\n title: 'Architecture',\r\n description: 'Vue d\\'ensemble de l\\'architecture fonctionnelle de SmartStack',\r\n href: '/system/docs/user/architecture',\r\n category: 'user',\r\n tags: ['architecture', 'modules', 'navigation', 'contextes'],\r\n icon: Layers,\r\n },\r\n {\r\n title: 'Modules',\r\n description: 'Liste des modules disponibles et leur configuration',\r\n href: '/system/docs/user/modules',\r\n category: 'user',\r\n tags: ['modules', 'fonctionnalites', 'features'],\r\n icon: FileText,\r\n },\r\n {\r\n title: 'Module Support',\r\n description: 'Documentation complete du module de ticketing et support client',\r\n href: '/system/docs/user/support',\r\n category: 'user',\r\n tags: ['tickets', 'support', 'SLA', 'escalation', 'templates'],\r\n icon: Headphones,\r\n },\r\n {\r\n title: 'Intelligence Artificielle',\r\n description: 'Configuration des providers IA, prompts et integrations',\r\n href: '/system/docs/user/ai',\r\n category: 'user',\r\n tags: ['IA', 'AI', 'prompts', 'OpenAI', 'Claude', 'providers'],\r\n icon: Bot,\r\n },\r\n {\r\n title: 'Parametres Systeme',\r\n description: 'Configuration globale et parametres de la plateforme',\r\n href: '/system/docs/user/settings',\r\n category: 'user',\r\n tags: ['settings', 'configuration', 'parametres', 'systeme'],\r\n icon: Settings,\r\n },\r\n {\r\n title: 'Presentation SmartStack',\r\n description: 'Presentation interactive de la plateforme SmartStack',\r\n href: '/system/docs/presentation',\r\n category: 'user',\r\n tags: ['presentation', 'slides', 'demo', 'overview'],\r\n icon: Presentation,\r\n },\r\n];\r\n\r\ninterface DocsSearchProps {\r\n readonly className?: string;\r\n readonly placeholder?: string;\r\n readonly onResultClick?: () => void;\r\n}\r\n\r\nexport function DocsSearch({ className = '', placeholder = 'Rechercher...', onResultClick }: DocsSearchProps): ReactElement {\r\n const [query, setQuery] = useState('');\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const resultsRef = useRef<HTMLDivElement>(null);\r\n const navigate = useNavigate();\r\n\r\n // Filtrer les resultats\r\n const results = useMemo(() => {\r\n if (!query.trim()) return [];\r\n\r\n const searchTerms = query.toLowerCase().split(' ').filter(Boolean);\r\n\r\n return docsIndex.filter((page) => {\r\n const searchableText = [\r\n page.title,\r\n page.description,\r\n ...page.tags,\r\n page.category,\r\n ].join(' ').toLowerCase();\r\n\r\n return searchTerms.every((term) => searchableText.includes(term));\r\n });\r\n }, [query]);\r\n\r\n // Reset selected index when results change\r\n useEffect(() => {\r\n setSelectedIndex(0);\r\n }, [results]);\r\n\r\n // Handle keyboard navigation\r\n const handleKeyDown = (e: React.KeyboardEvent) => {\r\n if (!isOpen || results.length === 0) return;\r\n\r\n switch (e.key) {\r\n case 'ArrowDown':\r\n e.preventDefault();\r\n setSelectedIndex((prev) => (prev + 1) % results.length);\r\n break;\r\n case 'ArrowUp':\r\n e.preventDefault();\r\n setSelectedIndex((prev) => (prev - 1 + results.length) % results.length);\r\n break;\r\n case 'Enter':\r\n e.preventDefault();\r\n if (results[selectedIndex]) {\r\n navigateToResult(results[selectedIndex]);\r\n }\r\n break;\r\n case 'Escape':\r\n setIsOpen(false);\r\n setQuery('');\r\n inputRef.current?.blur();\r\n break;\r\n }\r\n };\r\n\r\n const navigateToResult = (page: DocPage) => {\r\n navigate(page.href);\r\n setQuery('');\r\n setIsOpen(false);\r\n onResultClick?.();\r\n };\r\n\r\n // Close dropdown when clicking outside\r\n useEffect(() => {\r\n const handleClickOutside = (e: MouseEvent) => {\r\n if (resultsRef.current && !resultsRef.current.contains(e.target as Node) &&\r\n inputRef.current && !inputRef.current.contains(e.target as Node)) {\r\n setIsOpen(false);\r\n }\r\n };\r\n\r\n document.addEventListener('mousedown', handleClickOutside);\r\n return () => document.removeEventListener('mousedown', handleClickOutside);\r\n }, []);\r\n\r\n const getCategoryLabel = (category: string) => {\r\n return category === 'developer' ? 'Developpeur' : 'Utilisateur';\r\n };\r\n\r\n const getCategoryColor = (category: string) => {\r\n return category === 'developer'\r\n ? 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400'\r\n : 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400';\r\n };\r\n\r\n return (\r\n <div className={`relative ${className}`}>\r\n {/* Search Input */}\r\n <div className=\"relative\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-tertiary)]\" />\r\n <input\r\n ref={inputRef}\r\n type=\"text\"\r\n value={query}\r\n onChange={(e) => {\r\n setQuery(e.target.value);\r\n setIsOpen(true);\r\n }}\r\n onFocus={() => setIsOpen(true)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n className=\"w-full pl-9 pr-8 py-2 text-sm bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary-400)] focus:border-transparent\"\r\n />\r\n {query && (\r\n <button\r\n onClick={() => {\r\n setQuery('');\r\n inputRef.current?.focus();\r\n }}\r\n className=\"absolute right-2 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-[var(--bg-hover)]\"\r\n >\r\n <X className=\"w-3 h-3 text-[var(--text-tertiary)]\" />\r\n </button>\r\n )}\r\n </div>\r\n\r\n {/* Results Dropdown */}\r\n {isOpen && query.trim() && (\r\n <div\r\n ref={resultsRef}\r\n className=\"absolute top-full left-0 right-0 mt-1 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded-lg shadow-lg overflow-hidden z-50 max-h-80 overflow-y-auto\"\r\n >\r\n {results.length === 0 ? (\r\n <div className=\"p-4 text-center text-[var(--text-secondary)] text-sm\">\r\n Aucun resultat pour \"{query}\"\r\n </div>\r\n ) : (\r\n <div className=\"py-1\">\r\n {results.map((page, index) => {\r\n const Icon = page.icon;\r\n return (\r\n <button\r\n key={page.href}\r\n onClick={() => navigateToResult(page)}\r\n onMouseEnter={() => setSelectedIndex(index)}\r\n className={`w-full flex items-start gap-3 px-3 py-2 text-left transition-colors ${\r\n index === selectedIndex\r\n ? 'bg-[var(--color-primary-600)]/10'\r\n : 'hover:bg-[var(--bg-hover)]'\r\n }`}\r\n >\r\n <div className={`w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0 ${\r\n index === selectedIndex\r\n ? 'bg-[var(--color-primary-600)] text-white'\r\n : 'bg-[var(--bg-secondary)]'\r\n }`}>\r\n <Icon className=\"w-4 h-4\" />\r\n </div>\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"flex items-center gap-2\">\r\n <span className=\"font-medium text-sm truncate\">{page.title}</span>\r\n <span className={`px-1.5 py-0.5 text-xs rounded ${getCategoryColor(page.category)}`}>\r\n {getCategoryLabel(page.category)}\r\n </span>\r\n </div>\r\n <p className=\"text-xs text-[var(--text-secondary)] truncate mt-0.5\">\r\n {page.description}\r\n </p>\r\n </div>\r\n <ArrowRight className={`w-4 h-4 flex-shrink-0 mt-2 ${\r\n index === selectedIndex\r\n ? 'text-[var(--color-primary-600)]'\r\n : 'text-[var(--text-tertiary)]'\r\n }`} />\r\n </button>\r\n );\r\n })}\r\n </div>\r\n )}\r\n\r\n {/* Keyboard hint */}\r\n <div className=\"px-3 py-2 border-t border-[var(--border-color)] bg-[var(--bg-secondary)] text-xs text-[var(--text-tertiary)] flex items-center gap-4\">\r\n <span><kbd className=\"px-1 py-0.5 rounded bg-[var(--bg-primary)] border border-[var(--border-color)]\">↑↓</kbd> naviguer</span>\r\n <span><kbd className=\"px-1 py-0.5 rounded bg-[var(--bg-primary)] border border-[var(--border-color)]\">↵</kbd> ouvrir</span>\r\n <span><kbd className=\"px-1 py-0.5 rounded bg-[var(--bg-primary)] border border-[var(--border-color)]\">esc</kbd> fermer</span>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n","import { Outlet, NavLink, useLocation, useSearchParams } from 'react-router-dom';\r\nimport { Header } from '@/components/layout/Header';\r\nimport { Footer } from '@/components/layout/Footer';\r\nimport { NavigationProvider } from '@/contexts/NavigationContext';\r\nimport { DocsSearch } from '@/components/docs/DocsSearch';\r\nimport {\r\n Book,\r\n Code,\r\n Users,\r\n BarChart3,\r\n ChevronRight,\r\n Menu,\r\n X,\r\n} from 'lucide-react';\r\nimport { useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\n\r\ninterface NavChild {\r\n title: string;\r\n href: string;\r\n children?: { title: string; href: string }[];\r\n}\r\n\r\ninterface NavItem {\r\n title: string;\r\n href: string;\r\n icon: React.ElementType;\r\n children?: NavChild[];\r\n}\r\n\r\nconst navigation: NavItem[] = [\r\n {\r\n title: 'Accueil',\r\n href: '/system/docs',\r\n icon: Book,\r\n },\r\n {\r\n title: 'Developpeur',\r\n href: '/system/docs/developer',\r\n icon: Code,\r\n children: [\r\n { title: 'Infrastructure', href: '/system/docs/developer/infrastructure' },\r\n { title: 'Installation', href: '/system/docs/developer/installation' },\r\n { title: 'Base de donnees', href: '/system/docs/developer/database' },\r\n { title: 'Dual-DbContext', href: '/system/docs/developer/dual-dbcontext' },\r\n { title: 'FK Cross-Schema', href: '/system/docs/developer/cross-schema-fk' },\r\n {\r\n title: 'Tenant',\r\n href: '/system/docs/developer/tenants',\r\n children: [\r\n { title: 'Mode Multi-Tenant', href: '/system/docs/developer/tenants/multi-tenant-mode' },\r\n { title: 'Tenant B2C', href: '/system/docs/developer/tenants/b2c' },\r\n ],\r\n },\r\n { title: 'Integration Teams', href: '/system/docs/developer/teams-integration' },\r\n { title: 'Tests de Securite', href: '/system/docs/developer/testing/security' },\r\n ],\r\n },\r\n {\r\n title: 'Utilisateur',\r\n href: '/system/docs/user',\r\n icon: Users,\r\n children: [\r\n { title: 'Architecture', href: '/system/docs/user/architecture' },\r\n { title: 'Modules', href: '/system/docs/user/modules' },\r\n ],\r\n },\r\n {\r\n title: 'Analyse metier',\r\n href: '/system/docs/ba',\r\n icon: BarChart3,\r\n },\r\n];\r\n\r\nfunction NavItemComponent({ item, isChild = false, depth = 0 }: { item: NavItem | NavChild | { title: string; href: string }; isChild?: boolean; depth?: number }) {\r\n const location = useLocation();\r\n const isActive = location.pathname === item.href;\r\n const Icon = 'icon' in item ? item.icon : null;\r\n\r\n return (\r\n <NavLink\r\n to={item.href}\r\n className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm transition-colors ${\r\n isActive\r\n ? 'bg-[var(--color-primary-600)] text-white'\r\n : 'text-[var(--text-secondary)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)]'\r\n } ${isChild ? 'ml-6' : ''} ${depth > 1 ? 'ml-12' : ''}`}\r\n >\r\n {Icon && <Icon className=\"w-4 h-4\" />}\r\n {!Icon && isChild && <ChevronRight className=\"w-3 h-3\" />}\r\n {item.title}\r\n </NavLink>\r\n );\r\n}\r\n\r\nexport function DocsLayout(): ReactElement | null {\r\n const [sidebarOpen, setSidebarOpen] = useState(false);\r\n const location = useLocation();\r\n const [searchParams] = useSearchParams();\r\n const isEmbedded = searchParams.get('embedded') === 'true';\r\n\r\n const isParentActive = (item: NavItem | NavChild) => {\r\n if (location.pathname === item.href) return true;\r\n if ('children' in item && Array.isArray(item.children)) {\r\n return item.children.some((child) => {\r\n if (location.pathname === child.href) return true;\r\n if ('children' in child && Array.isArray(child.children)) {\r\n return child.children.some((subChild: { href: string }) => location.pathname === subChild.href);\r\n }\r\n return false;\r\n });\r\n }\r\n return false;\r\n };\r\n\r\n const isChildActive = (child: NavChild) => {\r\n if (location.pathname === child.href) return true;\r\n if (child.children) {\r\n return child.children.some((subChild) => location.pathname === subChild.href);\r\n }\r\n return false;\r\n };\r\n\r\n const renderSubChildren = (child: NavChild) => {\r\n if (child.children && isChildActive(child)) {\r\n return (\r\n <div className=\"mt-1 space-y-1\">\r\n {child.children.map((subChild) => (\r\n <NavItemComponent key={subChild.href} item={subChild} isChild depth={2} />\r\n ))}\r\n </div>\r\n );\r\n }\r\n return null;\r\n };\r\n\r\n const renderChildren = (item: NavItem) => {\r\n if (item.children && isParentActive(item)) {\r\n return (\r\n <div className=\"mt-1 space-y-1\">\r\n {item.children.map((child) => (\r\n <div key={child.href}>\r\n <NavItemComponent item={child} isChild />\r\n {renderSubChildren(child)}\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n }\r\n return null;\r\n };\r\n\r\n const Sidebar = () => (\r\n <nav className=\"space-y-1\">\r\n {navigation.map((item) => (\r\n <div key={item.href}>\r\n <NavItemComponent item={item} />\r\n {renderChildren(item)}\r\n </div>\r\n ))}\r\n </nav>\r\n );\r\n\r\n // Embedded mode: only show the content without header, sidebar, footer\r\n if (isEmbedded) {\r\n return (\r\n <div className=\"min-h-screen bg-[var(--bg-primary)]\">\r\n <main className=\"p-4 lg:p-6\">\r\n <div className=\"max-w-4xl mx-auto\">\r\n <Outlet />\r\n </div>\r\n </main>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <NavigationProvider>\r\n <div className=\"min-h-screen bg-[var(--bg-primary)] flex flex-col\">\r\n <Header />\r\n\r\n <div className=\"flex-1 flex pt-16\">\r\n {/* Mobile sidebar toggle */}\r\n <button\r\n className=\"lg:hidden fixed bottom-4 right-4 z-50 p-3 rounded-full bg-[var(--color-primary-600)] text-white shadow-lg\"\r\n onClick={() => setSidebarOpen(!sidebarOpen)}\r\n >\r\n {sidebarOpen ? <X className=\"w-6 h-6\" /> : <Menu className=\"w-6 h-6\" />}\r\n </button>\r\n\r\n {/* Mobile sidebar overlay */}\r\n {sidebarOpen && (\r\n <button\r\n type=\"button\"\r\n className=\"lg:hidden fixed inset-0 z-40 bg-black/50\"\r\n onClick={() => setSidebarOpen(false)}\r\n aria-label=\"Close sidebar\"\r\n />\r\n )}\r\n\r\n {/* Sidebar */}\r\n <aside\r\n className={`fixed lg:sticky top-16 z-40 lg:z-0 h-[calc(100vh-4rem)] w-64 bg-[var(--bg-secondary)] border-r border-[var(--border-color)] overflow-y-auto transition-transform lg:translate-x-0 ${\r\n sidebarOpen ? 'translate-x-0' : '-translate-x-full'\r\n }`}\r\n >\r\n <div className=\"p-4\">\r\n <h2 className=\"text-lg font-bold mb-4 flex items-center gap-2\">\r\n <Book className=\"w-5 h-5 text-[var(--color-primary-600)]\" />\r\n Documentation\r\n </h2>\r\n <DocsSearch\r\n className=\"mb-4\"\r\n placeholder=\"Rechercher dans la doc...\"\r\n onResultClick={() => setSidebarOpen(false)}\r\n />\r\n <Sidebar />\r\n </div>\r\n </aside>\r\n\r\n {/* Main content */}\r\n <main className=\"flex-1 min-w-0 p-6 lg:p-8\">\r\n <div className=\"max-w-4xl mx-auto\">\r\n <Outlet />\r\n </div>\r\n </main>\r\n </div>\r\n\r\n <Footer />\r\n </div>\r\n </NavigationProvider>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { Outlet } from 'react-router-dom';\r\nimport { Header } from '@/components/layout/Header';\r\nimport { Footer } from '@/components/layout/Footer';\r\nimport { NavigationProvider } from '@/contexts/NavigationContext';\r\n\r\nexport function PublicLayout(): ReactElement {\r\n return (\r\n <NavigationProvider>\r\n <div className=\"min-h-screen bg-[var(--bg-primary)] flex flex-col\">\r\n <Header />\r\n <main className=\"flex-1 pt-16\">\r\n <Outlet />\r\n </main>\r\n <Footer />\r\n </div>\r\n </NavigationProvider>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { ChevronRight, Home } from 'lucide-react';\r\nimport { Link } from 'react-router-dom';\r\n\r\nexport interface BreadcrumbItem {\r\n readonly label: string;\r\n readonly href?: string;\r\n readonly icon?: React.ReactNode;\r\n}\r\n\r\ninterface BreadcrumbProps {\r\n readonly items: BreadcrumbItem[];\r\n readonly className?: string;\r\n}\r\n\r\nexport function Breadcrumb({ items, className = '' }: BreadcrumbProps): ReactElement {\r\n return (\r\n <nav\r\n aria-label=\"Breadcrumb\"\r\n className={`flex items-center gap-1 text-sm ${className}`}\r\n >\r\n {items.map((item, index) => {\r\n const isLast = index === items.length - 1;\r\n const isFirst = index === 0;\r\n\r\n return (\r\n <div key={`${index}-${item.label}`} className=\"flex items-center gap-1\">\r\n {index > 0 && (\r\n <ChevronRight className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n )}\r\n {isLast && (\r\n <span className=\"flex items-center gap-1.5 text-[var(--text-primary)] font-medium\">\r\n {item.icon}\r\n {item.label}\r\n </span>\r\n )}\r\n {!isLast && item.href && (\r\n <Link\r\n to={item.href}\r\n className=\"flex items-center gap-1.5 text-[var(--text-secondary)] hover:text-[var(--color-accent-600)] transition-colors\"\r\n >\r\n {isFirst && !item.icon && <Home className=\"w-4 h-4\" />}\r\n {item.icon}\r\n {item.label}\r\n </Link>\r\n )}\r\n {!isLast && !item.href && (\r\n <span className=\"flex items-center gap-1.5 text-[var(--text-secondary)]\">\r\n {item.icon}\r\n {item.label}\r\n </span>\r\n )}\r\n </div>\r\n );\r\n })}\r\n </nav>\r\n );\r\n}\r\n","import { useState, useMemo, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport {\r\n ChevronUp, ChevronDown, ChevronsUpDown,\r\n ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight,\r\n Search, X, Loader2\r\n} from 'lucide-react';\r\n\r\n/**\r\n * DataTable - Composant de tableau de données standardisé SmartStack\r\n *\r\n * Fonctionnalités:\r\n * - Tri par colonnes\r\n * - Pagination\r\n * - Recherche globale\r\n * - Colonnes personnalisables\r\n * - États vide/chargement\r\n * - Sélection de lignes (optionnel)\r\n *\r\n * Usage:\r\n * ```tsx\r\n * <DataTable\r\n * data={users}\r\n * columns={[\r\n * { key: 'name', label: 'Nom', sortable: true },\r\n * { key: 'email', label: 'Email', sortable: true },\r\n * { key: 'role', label: 'Rôle', render: (user) => <Badge>{user.role}</Badge> },\r\n * ]}\r\n * pagination={{ pageSize: 10 }}\r\n * searchable\r\n * onRowClick={(user) => navigate(`/users/${user.id}`)}\r\n * />\r\n * ```\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface DataTableColumn<T> {\r\n /** Unique key (matches data field or custom) */\r\n key: string;\r\n /** Column header label */\r\n label: string;\r\n /** Is column sortable */\r\n sortable?: boolean;\r\n /** Custom render function */\r\n render?: (item: T, index: number) => ReactNode;\r\n /** Column width (CSS value) */\r\n width?: string;\r\n /** Column alignment */\r\n align?: 'left' | 'center' | 'right';\r\n /** Hide on mobile */\r\n hideOnMobile?: boolean;\r\n /** Sticky column */\r\n sticky?: 'left' | 'right';\r\n}\r\n\r\nexport interface DataTablePagination {\r\n /** Items per page */\r\n pageSize: number;\r\n /** Available page sizes */\r\n pageSizeOptions?: number[];\r\n /** Show page size selector */\r\n showSizeSelector?: boolean;\r\n}\r\n\r\nexport interface DataTableProps<T> {\r\n /** Data array */\r\n data: T[];\r\n /** Column definitions */\r\n columns: DataTableColumn<T>[];\r\n /** Loading state */\r\n loading?: boolean;\r\n /** Enable global search */\r\n searchable?: boolean;\r\n /** Search placeholder */\r\n searchPlaceholder?: string;\r\n /** Pagination config */\r\n pagination?: DataTablePagination;\r\n /** Row click handler */\r\n onRowClick?: (item: T, index: number) => void;\r\n /** Get unique key for each row */\r\n getRowKey?: (item: T, index: number) => string;\r\n /** Empty state message */\r\n emptyMessage?: string;\r\n /** Empty state icon */\r\n emptyIcon?: ReactNode;\r\n /** Custom header actions */\r\n headerActions?: ReactNode;\r\n /** Stripe rows */\r\n striped?: boolean;\r\n /** Compact mode */\r\n compact?: boolean;\r\n /** Custom className */\r\n className?: string;\r\n /** Enable row selection */\r\n selectable?: boolean;\r\n /** Selected row keys */\r\n selectedKeys?: Set<string>;\r\n /** Selection change handler */\r\n onSelectionChange?: (keys: Set<string>) => void;\r\n /** Custom search filter function */\r\n searchFilter?: (item: T, searchTerm: string) => boolean;\r\n /** Default sort column */\r\n defaultSortKey?: string;\r\n /** Default sort direction */\r\n defaultSortDirection?: 'asc' | 'desc';\r\n}\r\n\r\n// ============================================================================\r\n// Helper Functions\r\n// ============================================================================\r\n\r\nfunction getNestedValue(obj: unknown, path: string): unknown {\r\n return path.split('.').reduce((acc: unknown, part: string) => {\r\n if (acc && typeof acc === 'object' && part in acc) {\r\n return (acc as Record<string, unknown>)[part];\r\n }\r\n return undefined;\r\n }, obj);\r\n}\r\n\r\nfunction getAlignmentClass(align?: 'left' | 'center' | 'right'): string {\r\n if (align === 'right') return 'justify-end';\r\n if (align === 'center') return 'justify-center';\r\n return '';\r\n}\r\n\r\nfunction defaultSearchFilter<T>(item: T, searchTerm: string): boolean {\r\n const search = searchTerm.toLowerCase();\r\n return Object.values(item as Record<string, unknown>).some((value) => {\r\n if (value === null || value === undefined) return false;\r\n return String(value).toLowerCase().includes(search);\r\n });\r\n}\r\n\r\n// ============================================================================\r\n// Component\r\n// ============================================================================\r\n\r\nexport function DataTable<T>({\r\n data,\r\n columns,\r\n loading = false,\r\n searchable = false,\r\n searchPlaceholder = 'Rechercher...',\r\n pagination,\r\n onRowClick,\r\n getRowKey = (_item, index) => String(index),\r\n emptyMessage = 'Aucune donnée',\r\n emptyIcon,\r\n headerActions,\r\n striped = true,\r\n compact = false,\r\n className = '',\r\n selectable = false,\r\n selectedKeys = new Set(),\r\n onSelectionChange,\r\n searchFilter = defaultSearchFilter,\r\n defaultSortKey,\r\n defaultSortDirection = 'asc',\r\n}: DataTableProps<T>): ReactElement {\r\n // State\r\n const [searchTerm, setSearchTerm] = useState('');\r\n const [sortKey, setSortKey] = useState<string | null>(defaultSortKey || null);\r\n const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(defaultSortDirection);\r\n const [currentPage, setCurrentPage] = useState(1);\r\n const [pageSize, setPageSize] = useState(pagination?.pageSize || 10);\r\n\r\n // Guard: ensure data is always a valid array\r\n const safeData = Array.isArray(data) ? data : [];\r\n\r\n // Filter data by search\r\n const filteredData = useMemo(() => {\r\n if (!searchTerm) return safeData;\r\n return safeData.filter((item) => searchFilter(item, searchTerm));\r\n }, [safeData, searchTerm, searchFilter]);\r\n\r\n // Sort data\r\n const sortedData = useMemo(() => {\r\n if (!sortKey) return filteredData;\r\n\r\n return [...filteredData].sort((a, b) => {\r\n const aValue = getNestedValue(a, sortKey);\r\n const bValue = getNestedValue(b, sortKey);\r\n\r\n if (aValue === bValue) return 0;\r\n if (aValue === null || aValue === undefined) return 1;\r\n if (bValue === null || bValue === undefined) return -1;\r\n\r\n const comparison = String(aValue).localeCompare(String(bValue), undefined, { numeric: true });\r\n return sortDirection === 'asc' ? comparison : -comparison;\r\n });\r\n }, [filteredData, sortKey, sortDirection]);\r\n\r\n // Paginate data\r\n const paginatedData = useMemo(() => {\r\n if (!pagination) return sortedData;\r\n const start = (currentPage - 1) * pageSize;\r\n return sortedData.slice(start, start + pageSize);\r\n }, [sortedData, pagination, currentPage, pageSize]);\r\n\r\n // Calculate pagination info\r\n const totalPages = pagination ? Math.ceil(sortedData.length / pageSize) : 1;\r\n const totalItems = sortedData.length;\r\n const startItem = pagination ? (currentPage - 1) * pageSize + 1 : 1;\r\n const endItem = pagination ? Math.min(currentPage * pageSize, totalItems) : totalItems;\r\n\r\n // Handlers\r\n const handleSort = (key: string) => {\r\n if (sortKey === key) {\r\n setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc'));\r\n } else {\r\n setSortKey(key);\r\n setSortDirection('asc');\r\n }\r\n };\r\n\r\n const handlePageChange = (page: number) => {\r\n setCurrentPage(Math.max(1, Math.min(page, totalPages)));\r\n };\r\n\r\n const handleSelectAll = () => {\r\n if (!onSelectionChange) return;\r\n const allKeys = paginatedData.map((item, index) => getRowKey(item, index));\r\n const allSelected = allKeys.every((key) => selectedKeys.has(key));\r\n\r\n if (allSelected) {\r\n const newKeys = new Set(selectedKeys);\r\n allKeys.forEach((key) => newKeys.delete(key));\r\n onSelectionChange(newKeys);\r\n } else {\r\n const newKeys = new Set(selectedKeys);\r\n allKeys.forEach((key) => newKeys.add(key));\r\n onSelectionChange(newKeys);\r\n }\r\n };\r\n\r\n const handleSelectRow = (key: string) => {\r\n if (!onSelectionChange) return;\r\n const newKeys = new Set(selectedKeys);\r\n if (newKeys.has(key)) {\r\n newKeys.delete(key);\r\n } else {\r\n newKeys.add(key);\r\n }\r\n onSelectionChange(newKeys);\r\n };\r\n\r\n // Reset page when search changes\r\n const handleSearchChange = (value: string) => {\r\n setSearchTerm(value);\r\n setCurrentPage(1);\r\n };\r\n\r\n // Styles\r\n const cellPadding = compact ? 'px-3 py-2' : 'px-4 py-3';\r\n const headerPadding = compact ? 'px-3 py-2' : 'px-4 py-3';\r\n\r\n // Render sort icon\r\n const renderSortIcon = (column: DataTableColumn<T>) => {\r\n if (!column.sortable) return null;\r\n\r\n if (sortKey === column.key) {\r\n return sortDirection === 'asc' ? (\r\n <ChevronUp className=\"w-4 h-4\" />\r\n ) : (\r\n <ChevronDown className=\"w-4 h-4\" />\r\n );\r\n }\r\n return <ChevronsUpDown className=\"w-4 h-4 opacity-30\" />;\r\n };\r\n\r\n return (\r\n <div className={`${className}`}>\r\n {/* Header with search and actions */}\r\n {(searchable || headerActions) && (\r\n <div className=\"flex items-center justify-between gap-4 mb-4\">\r\n {searchable && (\r\n <div className=\"relative flex-1 max-w-md\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-secondary)] pointer-events-none\" />\r\n <input\r\n type=\"text\"\r\n value={searchTerm}\r\n onChange={(e) => handleSearchChange(e.target.value)}\r\n placeholder={searchPlaceholder}\r\n className=\"input w-full pl-10 pr-10\"\r\n />\r\n {searchTerm && (\r\n <button\r\n onClick={() => handleSearchChange('')}\r\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-[var(--text-secondary)] hover:text-[var(--text-primary)]\"\r\n >\r\n <X className=\"w-4 h-4\" />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n {headerActions && <div className=\"flex items-center gap-2\">{headerActions}</div>}\r\n </div>\r\n )}\r\n\r\n {/* Table */}\r\n <div className=\"overflow-x-auto border border-[var(--border-color)] rounded-lg\">\r\n <table className=\"w-full\">\r\n <thead className=\"bg-[var(--bg-secondary)] border-b border-[var(--border-color)]\">\r\n <tr>\r\n {selectable && (\r\n <th className={`${headerPadding} w-12`}>\r\n <input\r\n type=\"checkbox\"\r\n checked={\r\n paginatedData.length > 0 &&\r\n paginatedData.every((item, index) => selectedKeys.has(getRowKey(item, index)))\r\n }\r\n onChange={handleSelectAll}\r\n className=\"rounded border-[var(--border-color)]\"\r\n />\r\n </th>\r\n )}\r\n {columns.map((column) => (\r\n <th\r\n key={column.key}\r\n className={`${headerPadding} text-${column.align || 'left'} text-sm font-medium text-[var(--text-secondary)] ${\r\n column.hideOnMobile ? 'hidden md:table-cell' : ''\r\n } ${column.sortable ? 'cursor-pointer hover:bg-[var(--bg-tertiary)] select-none' : ''}`}\r\n style={{ width: column.width }}\r\n onClick={() => column.sortable && handleSort(column.key)}\r\n >\r\n <div className={`flex items-center gap-1 ${getAlignmentClass(column.align)}`}>\r\n <span>{column.label}</span>\r\n {renderSortIcon(column)}\r\n </div>\r\n </th>\r\n ))}\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {loading && (\r\n <tr>\r\n <td colSpan={columns.length + (selectable ? 1 : 0)} className=\"py-12 text-center\">\r\n <Loader2 className=\"w-8 h-8 mx-auto animate-spin text-[var(--color-primary-500)]\" />\r\n </td>\r\n </tr>\r\n )}\r\n {!loading && paginatedData.length === 0 && (\r\n <tr>\r\n <td colSpan={columns.length + (selectable ? 1 : 0)} className=\"py-12 text-center\">\r\n {emptyIcon && <div className=\"mb-3\">{emptyIcon}</div>}\r\n <p className=\"text-[var(--text-secondary)]\">{emptyMessage}</p>\r\n </td>\r\n </tr>\r\n )}\r\n {!loading && paginatedData.length > 0 && (\r\n paginatedData.map((item, index) => {\r\n const rowKey = getRowKey(item, index);\r\n const isSelected = selectedKeys.has(rowKey);\r\n const globalIndex = (currentPage - 1) * pageSize + index;\r\n\r\n return (\r\n <tr\r\n key={rowKey}\r\n onClick={() => onRowClick?.(item, globalIndex)}\r\n className={`\r\n border-b border-[var(--border-color)] last:border-b-0\r\n ${striped && index % 2 === 1 ? 'bg-[var(--bg-secondary)]/50' : ''}\r\n ${onRowClick ? 'cursor-pointer hover:bg-[var(--bg-tertiary)]' : ''}\r\n ${isSelected ? 'bg-[var(--accent-bg)]' : ''}\r\n transition-colors\r\n `}\r\n >\r\n {selectable && (\r\n <td className={`${cellPadding} w-12`} onClick={(e) => e.stopPropagation()}>\r\n <input\r\n type=\"checkbox\"\r\n checked={isSelected}\r\n onChange={() => handleSelectRow(rowKey)}\r\n className=\"rounded border-[var(--border-color)]\"\r\n />\r\n </td>\r\n )}\r\n {columns.map((column) => (\r\n <td\r\n key={column.key}\r\n className={`${cellPadding} text-${column.align || 'left'} ${\r\n column.hideOnMobile ? 'hidden md:table-cell' : ''\r\n }`}\r\n >\r\n {column.render\r\n ? column.render(item, globalIndex)\r\n : String(getNestedValue(item, column.key) ?? '')}\r\n </td>\r\n ))}\r\n </tr>\r\n );\r\n })\r\n )}\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n {/* Pagination */}\r\n {pagination && totalItems > 0 && (\r\n <div className=\"flex items-center justify-between mt-4\">\r\n <div className=\"flex items-center gap-4\">\r\n <span className=\"text-sm text-[var(--text-secondary)]\">\r\n {startItem}-{endItem} sur {totalItems}\r\n </span>\r\n {pagination.showSizeSelector && (\r\n <select\r\n value={pageSize}\r\n onChange={(e) => {\r\n setPageSize(Number(e.target.value));\r\n setCurrentPage(1);\r\n }}\r\n className=\"input py-1 text-sm\"\r\n >\r\n {(pagination.pageSizeOptions || [10, 25, 50, 100]).map((size) => (\r\n <option key={size} value={size}>\r\n {size} / page\r\n </option>\r\n ))}\r\n </select>\r\n )}\r\n </div>\r\n\r\n <div className=\"flex items-center gap-1\">\r\n <button\r\n onClick={() => handlePageChange(1)}\r\n disabled={currentPage === 1}\r\n className=\"btn btn-ghost p-2 disabled:opacity-30\"\r\n title=\"Première page\"\r\n >\r\n <ChevronsLeft className=\"w-4 h-4\" />\r\n </button>\r\n <button\r\n onClick={() => handlePageChange(currentPage - 1)}\r\n disabled={currentPage === 1}\r\n className=\"btn btn-ghost p-2 disabled:opacity-30\"\r\n title=\"Page précédente\"\r\n >\r\n <ChevronLeft className=\"w-4 h-4\" />\r\n </button>\r\n\r\n <span className=\"px-3 text-sm\">\r\n Page {currentPage} / {totalPages}\r\n </span>\r\n\r\n <button\r\n onClick={() => handlePageChange(currentPage + 1)}\r\n disabled={currentPage === totalPages}\r\n className=\"btn btn-ghost p-2 disabled:opacity-30\"\r\n title=\"Page suivante\"\r\n >\r\n <ChevronRight className=\"w-4 h-4\" />\r\n </button>\r\n <button\r\n onClick={() => handlePageChange(totalPages)}\r\n disabled={currentPage === totalPages}\r\n className=\"btn btn-ghost p-2 disabled:opacity-30\"\r\n title=\"Dernière page\"\r\n >\r\n <ChevronsRight className=\"w-4 h-4\" />\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default DataTable;\r\n","import { useState, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { LayoutList, LayoutGrid, Columns3, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';\r\n\r\nexport type ViewMode = 'table' | 'cards' | 'kanban';\r\n\r\ninterface DataViewProps<T> {\r\n data: T[];\r\n viewMode?: ViewMode;\r\n onViewModeChange?: (mode: ViewMode) => void;\r\n // Render functions for each view\r\n renderTable?: (data: T[]) => ReactNode;\r\n renderCards?: (data: T[]) => ReactNode;\r\n renderKanban?: (data: T[]) => ReactNode;\r\n // View visibility\r\n showTableView?: boolean;\r\n showCardsView?: boolean;\r\n showKanbanView?: boolean;\r\n // Labels\r\n tableLabel?: string;\r\n cardsLabel?: string;\r\n kanbanLabel?: string;\r\n}\r\n\r\nexport function DataView<T>({\r\n data,\r\n viewMode: controlledViewMode,\r\n onViewModeChange,\r\n renderTable,\r\n renderCards,\r\n renderKanban,\r\n showTableView = true,\r\n showCardsView = true,\r\n showKanbanView = true,\r\n tableLabel = 'Table',\r\n cardsLabel = 'Cards',\r\n kanbanLabel = 'Kanban',\r\n}: DataViewProps<T>): ReactElement {\r\n const [internalViewMode, setInternalViewMode] = useState<ViewMode>('table');\r\n\r\n const viewMode = controlledViewMode ?? internalViewMode;\r\n const setViewMode = onViewModeChange ?? setInternalViewMode;\r\n\r\n const views: Array<{ mode: ViewMode; label: string; icon: typeof LayoutList; show: boolean }> = [\r\n { mode: 'table', label: tableLabel, icon: LayoutList, show: showTableView },\r\n { mode: 'cards', label: cardsLabel, icon: LayoutGrid, show: showCardsView },\r\n { mode: 'kanban', label: kanbanLabel, icon: Columns3, show: showKanbanView },\r\n ];\r\n const visibleViews = views.filter(v => v.show);\r\n\r\n return (\r\n <div className=\"space-y-4\">\r\n {/* View Toggle */}\r\n {visibleViews.length > 1 && (\r\n <div className=\"flex items-center gap-1 p-1 rounded-[var(--radius-button)] bg-[var(--bg-secondary)] w-fit\">\r\n {visibleViews.map(({ mode, label, icon: Icon }) => (\r\n <button\r\n key={mode}\r\n onClick={() => setViewMode(mode)}\r\n className={`px-3 py-1.5 rounded-[var(--radius-button)] text-sm font-medium flex items-center gap-2 transition-all ${\r\n viewMode === mode\r\n ? 'bg-[var(--color-accent-600)] text-white shadow-sm'\r\n : 'text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)]'\r\n }`}\r\n >\r\n <Icon className=\"w-4 h-4\" />\r\n <span className=\"hidden sm:inline\">{label}</span>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Content */}\r\n <div>\r\n {viewMode === 'table' && renderTable && renderTable(data)}\r\n {viewMode === 'cards' && renderCards && renderCards(data)}\r\n {viewMode === 'kanban' && renderKanban && renderKanban(data)}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// Standalone View Toggle component for custom layouts\r\ninterface ViewToggleProps {\r\n readonly viewMode: ViewMode;\r\n readonly onChange: (mode: ViewMode) => void;\r\n readonly showTable?: boolean;\r\n readonly showCards?: boolean;\r\n readonly showKanban?: boolean;\r\n readonly tableLabel?: string;\r\n readonly cardsLabel?: string;\r\n readonly kanbanLabel?: string;\r\n}\r\n\r\nexport function ViewToggle({\r\n viewMode,\r\n onChange,\r\n showTable = true,\r\n showCards = true,\r\n showKanban = true,\r\n tableLabel = 'Table',\r\n cardsLabel = 'Cards',\r\n kanbanLabel = 'Kanban',\r\n}: ViewToggleProps): ReactElement | null {\r\n const allViews: Array<{ mode: ViewMode; label: string; icon: typeof LayoutList; show: boolean }> = [\r\n { mode: 'table', label: tableLabel, icon: LayoutList, show: showTable },\r\n { mode: 'cards', label: cardsLabel, icon: LayoutGrid, show: showCards },\r\n { mode: 'kanban', label: kanbanLabel, icon: Columns3, show: showKanban },\r\n ];\r\n const views = allViews.filter(v => v.show);\r\n\r\n if (views.length <= 1) return null;\r\n\r\n return (\r\n <div className=\"flex items-center gap-1 p-1 rounded-[var(--radius-button)] bg-[var(--bg-secondary)] border border-[var(--border-color)]\">\r\n {views.map(({ mode, label, icon: Icon }) => (\r\n <button\r\n key={mode}\r\n onClick={() => onChange(mode)}\r\n className={`px-3 py-1.5 rounded-[var(--radius-button)] text-sm font-medium flex items-center gap-2 transition-all ${\r\n viewMode === mode\r\n ? 'bg-[var(--color-accent-600)] text-white shadow-sm'\r\n : 'text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)]'\r\n }`}\r\n >\r\n <Icon className=\"w-4 h-4\" />\r\n <span className=\"hidden sm:inline\">{label}</span>\r\n </button>\r\n ))}\r\n </div>\r\n );\r\n}\r\n\r\n// Avatar component with gradient colors\r\ninterface AvatarProps {\r\n readonly name: string;\r\n readonly size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\r\n readonly className?: string;\r\n}\r\n\r\nconst AVATAR_GRADIENTS = [\r\n 'from-violet-500 to-purple-500',\r\n 'from-blue-500 to-cyan-500',\r\n 'from-emerald-500 to-teal-500',\r\n 'from-orange-500 to-amber-500',\r\n 'from-pink-500 to-rose-500',\r\n 'from-indigo-500 to-blue-500',\r\n 'from-fuchsia-500 to-pink-500',\r\n 'from-cyan-500 to-blue-500',\r\n];\r\n\r\nfunction stringToGradient(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n hash = str.charCodeAt(i) + ((hash << 5) - hash);\r\n }\r\n return AVATAR_GRADIENTS[Math.abs(hash) % AVATAR_GRADIENTS.length];\r\n}\r\n\r\nfunction getInitials(name: string): string {\r\n return name\r\n .split(' ')\r\n .map(n => n.at(0))\r\n .join('')\r\n .toUpperCase()\r\n .slice(0, 2);\r\n}\r\n\r\nexport function Avatar({ name, size = 'md', className = '' }: AvatarProps): ReactElement {\r\n const sizeClasses = {\r\n xs: 'w-6 h-6 text-xs',\r\n sm: 'w-8 h-8 text-xs',\r\n md: 'w-10 h-10 text-sm',\r\n lg: 'w-12 h-12 text-base',\r\n xl: 'w-16 h-16 text-lg',\r\n };\r\n\r\n const shadowClasses = {\r\n xs: '',\r\n sm: 'shadow-md',\r\n md: 'shadow-lg shadow-primary-500/20',\r\n lg: 'shadow-lg shadow-primary-500/20',\r\n xl: 'shadow-xl shadow-primary-500/25',\r\n };\r\n\r\n return (\r\n <div\r\n className={`${sizeClasses[size]} ${shadowClasses[size]} rounded-full bg-gradient-to-br ${stringToGradient(name)} flex items-center justify-center text-white font-semibold ${className}`}\r\n >\r\n {getInitials(name)}\r\n </div>\r\n );\r\n}\r\n\r\n// Status Badge component\r\ninterface StatusBadgeProps {\r\n readonly status: 'active' | 'inactive' | 'pending' | 'warning' | 'error';\r\n readonly label: string;\r\n readonly showDot?: boolean;\r\n readonly size?: 'sm' | 'md';\r\n}\r\n\r\nexport function StatusBadge({ status, label, showDot = true, size = 'md' }: StatusBadgeProps): ReactElement {\r\n const statusStyles = {\r\n active: 'bg-[var(--success-bg)] text-[var(--success-text)] border-[var(--success-border)]',\r\n inactive: 'bg-[var(--bg-tertiary)] text-[var(--text-tertiary)] border-[var(--border-color)]',\r\n pending: 'bg-[var(--warning-bg)] text-[var(--warning-text)] border-[var(--warning-border)]',\r\n warning: 'bg-[var(--warning-bg)] text-[var(--warning-text)] border-[var(--warning-border)]',\r\n error: 'bg-[var(--error-bg)] text-[var(--error-text)] border-[var(--error-border)]',\r\n };\r\n\r\n const dotColors = {\r\n active: 'bg-[var(--success-dot)]',\r\n inactive: 'bg-[var(--text-muted)]',\r\n pending: 'bg-[var(--warning-dot)]',\r\n warning: 'bg-[var(--warning-dot)]',\r\n error: 'bg-[var(--error-dot)]',\r\n };\r\n\r\n const sizeClasses = {\r\n sm: 'px-2 py-0.5 text-xs',\r\n md: 'px-2.5 py-1 text-xs',\r\n };\r\n\r\n return (\r\n <span className={`inline-flex items-center gap-1.5 ${sizeClasses[size]} rounded-[var(--radius-badge)] font-medium border ${statusStyles[status]}`}>\r\n {showDot && <span className={`w-1.5 h-1.5 rounded-full ${dotColors[status]}`} />}\r\n {label}\r\n </span>\r\n );\r\n}\r\n\r\n// Role Badge component\r\ninterface RoleBadgeProps {\r\n readonly role: string;\r\n readonly variant?: 'accent' | 'default';\r\n readonly icon?: ReactNode;\r\n}\r\n\r\nexport function RoleBadge({ role, variant = 'default', icon }: RoleBadgeProps): ReactElement {\r\n const variantStyles = {\r\n accent: 'bg-[var(--accent-bg)] text-[var(--accent-text)] border-[var(--accent-border)]',\r\n default: 'bg-[var(--bg-secondary)] text-[var(--text-secondary)] border-[var(--border-color)]',\r\n };\r\n\r\n return (\r\n <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-[var(--radius-badge)] text-xs font-medium border ${variantStyles[variant]}`}>\r\n {icon}\r\n {role}\r\n </span>\r\n );\r\n}\r\n\r\n// Card wrapper for Cards view\r\ninterface UserCardProps {\r\n readonly children: ReactNode;\r\n readonly onClick?: () => void;\r\n readonly className?: string;\r\n}\r\n\r\nexport function DataCard({ children, onClick, className = '' }: UserCardProps): ReactElement {\r\n if (onClick) {\r\n return (\r\n <button\r\n type=\"button\"\r\n onClick={onClick}\r\n className={`bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)] p-5 transition-all hover:shadow-lg hover:border-[var(--color-accent-500)]/50 cursor-pointer text-left w-full ${className}`}\r\n >\r\n {children}\r\n </button>\r\n );\r\n }\r\n return (\r\n <div\r\n className={`bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)] p-5 transition-all hover:shadow-lg hover:border-[var(--color-accent-500)]/50 ${className}`}\r\n >\r\n {children}\r\n </div>\r\n );\r\n}\r\n\r\n// Kanban Column wrapper\r\ninterface KanbanColumnProps {\r\n readonly title: string;\r\n readonly count: number;\r\n readonly color: 'success' | 'warning' | 'error' | 'default';\r\n readonly children: ReactNode;\r\n readonly isDragOver?: boolean;\r\n readonly isDropDisabled?: boolean;\r\n readonly onDragOver?: (e: React.DragEvent) => void;\r\n readonly onDragLeave?: () => void;\r\n readonly onDrop?: (e: React.DragEvent) => void;\r\n}\r\n\r\nexport function KanbanColumn({ title, count, color, children, isDragOver, isDropDisabled, onDragOver, onDragLeave, onDrop }: KanbanColumnProps): ReactElement {\r\n const colorClasses = {\r\n success: 'bg-[var(--success-dot)]',\r\n warning: 'bg-[var(--warning-dot)]',\r\n error: 'bg-[var(--error-dot)]',\r\n default: 'bg-[var(--text-muted)]',\r\n };\r\n\r\n return (\r\n <div\r\n className={`min-w-[280px] flex-1 rounded-lg p-2 transition-colors ${\r\n isDragOver && !isDropDisabled ? 'bg-[var(--color-accent-500)]/10 ring-2 ring-[var(--color-accent-500)]/50' : ''\r\n } ${isDropDisabled ? 'opacity-40' : ''}`}\r\n onDragOver={isDropDisabled ? undefined : onDragOver}\r\n onDragLeave={onDragLeave}\r\n onDrop={isDropDisabled ? undefined : onDrop}\r\n >\r\n <div className=\"flex items-center gap-2 mb-4\">\r\n <span className={`w-2 h-2 rounded-full ${colorClasses[color]}`} />\r\n <h3 className=\"font-semibold text-[var(--text-primary)]\">{title}</h3>\r\n <span className=\"px-2 py-0.5 rounded-full text-xs font-medium bg-[var(--bg-secondary)] text-[var(--text-secondary)]\">\r\n {count}\r\n </span>\r\n </div>\r\n <div className=\"space-y-3 min-h-[100px]\">\r\n {children}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// Kanban Card wrapper\r\ninterface KanbanCardProps {\r\n readonly children: ReactNode;\r\n readonly onClick?: () => void;\r\n readonly draggable?: boolean;\r\n readonly isDragging?: boolean;\r\n readonly onDragStart?: () => void;\r\n readonly onDragEnd?: () => void;\r\n}\r\n\r\nexport function KanbanCard({ children, onClick, draggable = true, isDragging, onDragStart, onDragEnd }: KanbanCardProps): ReactElement {\r\n return (\r\n <div\r\n draggable={draggable}\r\n onClick={onClick}\r\n onKeyDown={onClick ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(); } } : undefined}\r\n role={onClick ? 'button' : undefined}\r\n tabIndex={onClick ? 0 : undefined}\r\n onDragStart={onDragStart}\r\n onDragEnd={onDragEnd}\r\n className={`bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)] p-4 transition-all hover:shadow-md hover:border-[var(--color-accent-500)]/30 cursor-grab active:cursor-grabbing ${\r\n isDragging ? 'opacity-50 scale-95' : ''\r\n }`}\r\n >\r\n {children}\r\n </div>\r\n );\r\n}\r\n\r\n// Pagination component\r\ninterface PaginationProps {\r\n readonly page: number;\r\n readonly totalPages: number;\r\n readonly totalCount: number;\r\n readonly pageSize?: number;\r\n readonly onPageChange: (page: number) => void;\r\n readonly itemLabel?: string;\r\n readonly showInfo?: boolean;\r\n readonly showFirstLast?: boolean;\r\n}\r\n\r\nexport function Pagination({\r\n page,\r\n totalPages,\r\n totalCount,\r\n pageSize = 20,\r\n onPageChange,\r\n itemLabel = 'éléments',\r\n showInfo = true,\r\n showFirstLast = true,\r\n}: PaginationProps): ReactElement {\r\n const startItem = ((page - 1) * pageSize) + 1;\r\n const endItem = Math.min(page * pageSize, totalCount);\r\n\r\n // Calculate visible page numbers\r\n const getPageNumbers = () => {\r\n const pages: (number | 'ellipsis')[] = [];\r\n\r\n if (totalPages <= 7) {\r\n for (let i = 1; i <= totalPages; i++) pages.push(i);\r\n } else {\r\n // Always show first page\r\n pages.push(1);\r\n\r\n if (page > 3) {\r\n pages.push('ellipsis');\r\n }\r\n\r\n // Pages around current\r\n const start = Math.max(2, page - 1);\r\n const end = Math.min(totalPages - 1, page + 1);\r\n\r\n for (let i = start; i <= end; i++) {\r\n if (!pages.includes(i)) pages.push(i);\r\n }\r\n\r\n if (page < totalPages - 2) {\r\n pages.push('ellipsis');\r\n }\r\n\r\n // Always show last page\r\n if (!pages.includes(totalPages)) pages.push(totalPages);\r\n }\r\n\r\n return pages;\r\n };\r\n\r\n return (\r\n <div className=\"card p-4\">\r\n <div className=\"flex flex-col sm:flex-row items-center justify-between gap-4\">\r\n {/* Info */}\r\n {showInfo && (\r\n <div className=\"text-sm text-[var(--text-secondary)]\">\r\n Affichage de{' '}\r\n <span className=\"font-medium text-[var(--text-primary)]\">{startItem}</span>\r\n {' '}à{' '}\r\n <span className=\"font-medium text-[var(--text-primary)]\">{endItem}</span>\r\n {' '}sur{' '}\r\n <span className=\"font-medium text-[var(--text-primary)]\">{totalCount}</span>\r\n {' '}{itemLabel}\r\n </div>\r\n )}\r\n\r\n {/* Controls */}\r\n {totalPages > 1 && (\r\n <div className=\"flex items-center gap-1\">\r\n {/* First page */}\r\n {showFirstLast && (\r\n <button\r\n onClick={() => onPageChange(1)}\r\n disabled={page === 1}\r\n className=\"hidden sm:flex btn btn-secondary p-2 disabled:opacity-40 disabled:cursor-not-allowed\"\r\n title=\"Première page\"\r\n >\r\n <ChevronsLeft className=\"w-4 h-4\" />\r\n </button>\r\n )}\r\n\r\n {/* Previous */}\r\n <button\r\n onClick={() => onPageChange(Math.max(1, page - 1))}\r\n disabled={page === 1}\r\n className=\"btn btn-secondary p-2 disabled:opacity-40 disabled:cursor-not-allowed\"\r\n title=\"Page précédente\"\r\n >\r\n <ChevronLeft className=\"w-5 h-5\" />\r\n </button>\r\n\r\n {/* Page numbers */}\r\n <div className=\"flex items-center gap-1\">\r\n {getPageNumbers().map((pageNum, index) =>\r\n pageNum === 'ellipsis' ? (\r\n <span key={`ellipsis-${index}`} className=\"px-2 text-[var(--text-muted)]\">\r\n ...\r\n </span>\r\n ) : (\r\n <button\r\n key={`page-${pageNum}`}\r\n onClick={() => onPageChange(pageNum)}\r\n className={`w-10 h-10 rounded-[var(--radius-button)] text-sm font-medium transition-all ${\r\n page === pageNum\r\n ? 'bg-[var(--color-accent-600)] text-white shadow-lg shadow-[var(--color-accent-500)]/30'\r\n : 'btn btn-secondary hover:bg-[var(--bg-hover)]'\r\n }`}\r\n >\r\n {pageNum}\r\n </button>\r\n )\r\n )}\r\n </div>\r\n\r\n {/* Next */}\r\n <button\r\n onClick={() => onPageChange(Math.min(totalPages, page + 1))}\r\n disabled={page === totalPages}\r\n className=\"btn btn-secondary p-2 disabled:opacity-40 disabled:cursor-not-allowed\"\r\n title=\"Page suivante\"\r\n >\r\n <ChevronRight className=\"w-5 h-5\" />\r\n </button>\r\n\r\n {/* Last page */}\r\n {showFirstLast && (\r\n <button\r\n onClick={() => onPageChange(totalPages)}\r\n disabled={page === totalPages}\r\n className=\"hidden sm:flex btn btn-secondary p-2 disabled:opacity-40 disabled:cursor-not-allowed\"\r\n title=\"Dernière page\"\r\n >\r\n <ChevronsRight className=\"w-4 h-4\" />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { type ReactNode, type ElementType } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { ExternalLink, BookOpen } from 'lucide-react';\r\n\r\n/**\r\n * EntityCard - Composant de card standardisé SmartStack\r\n *\r\n * Design de référence: /administration/ai/settings (Provider cards)\r\n *\r\n * Caractéristiques:\r\n * - Header coloré distinct avec avatar carré arrondi\r\n * - Badge avec tooltip en haut à droite du header\r\n * - Corps avec description, stats, liens\r\n * - Boutons d'action en bas (push to bottom avec flex)\r\n * - Support des couleurs d'accent dynamiques\r\n *\r\n * Usage:\r\n * ```tsx\r\n * <EntityCard\r\n * avatar={{ letter: 'O', color: '#10a37f' }}\r\n * title=\"OpenAI\"\r\n * subtitle=\"openai\"\r\n * description=\"OpenAI GPT models (GPT-4, GPT-4o, GPT-3.5)\"\r\n * stats=\"15 modèle(s)\"\r\n * badge={{ icon: Shield, tooltip: 'API Admin supportée' }}\r\n * links={[\r\n * { icon: ExternalLink, label: 'Site officiel', href: 'https://openai.com' },\r\n * { icon: BookOpen, label: 'Documentation API', href: 'https://platform.openai.com/docs' },\r\n * ]}\r\n * actions={[\r\n * { label: 'Obtenir une clé API', href: 'https://...', variant: 'primary', icon: Key },\r\n * { label: 'Obtenir une clé API Admin', href: 'https://...', variant: 'secondary', icon: Shield },\r\n * ]}\r\n * />\r\n * ```\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface EntityCardAvatar {\r\n /** Single letter to display */\r\n readonly letter: string;\r\n /** Background color (CSS color) */\r\n readonly color: string;\r\n /** Optional image URL (replaces letter if provided) */\r\n readonly imageUrl?: string;\r\n}\r\n\r\nexport interface EntityCardBadge {\r\n /** Icon component */\r\n readonly icon?: ElementType;\r\n /** Tooltip text */\r\n readonly tooltip?: string;\r\n /** Custom color override */\r\n readonly color?: string;\r\n}\r\n\r\nexport interface EntityCardLink {\r\n /** Icon component */\r\n readonly icon: ElementType;\r\n /** Link label */\r\n readonly label: string;\r\n /** URL (opens in new tab) */\r\n readonly href?: string;\r\n /** Or onClick handler */\r\n readonly onClick?: () => void;\r\n}\r\n\r\nexport interface EntityCardAction {\r\n /** Button label */\r\n readonly label: string;\r\n /** URL (makes it an <a> tag) */\r\n readonly href?: string;\r\n /** Or onClick handler (makes it a <button>) */\r\n readonly onClick?: () => void;\r\n /** Button variant */\r\n readonly variant?: 'primary' | 'secondary' | 'ghost';\r\n /** Optional icon */\r\n readonly icon?: ElementType;\r\n /** Disabled state */\r\n readonly disabled?: boolean;\r\n /** Loading state */\r\n readonly loading?: boolean;\r\n}\r\n\r\nexport interface EntityCardTag {\r\n /** Tag label */\r\n readonly label: string;\r\n /** Tag variant/color */\r\n readonly variant?: 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info';\r\n /** Click handler (makes tag interactive) */\r\n readonly onClick?: () => void;\r\n}\r\n\r\nexport interface EntityCardProps {\r\n /** Avatar configuration */\r\n readonly avatar?: EntityCardAvatar;\r\n /** Card title */\r\n readonly title: string;\r\n /** Subtitle/code below title */\r\n readonly subtitle?: string;\r\n /** Description text */\r\n readonly description?: string | ReactNode;\r\n /** Stats text (e.g., \"15 modèle(s)\") */\r\n readonly stats?: string | ReactNode;\r\n /** Optional badge in header top-right */\r\n readonly badge?: EntityCardBadge;\r\n /** Array of link items */\r\n readonly links?: EntityCardLink[];\r\n /** Array of action buttons */\r\n readonly actions?: EntityCardAction[];\r\n /** Array of tags */\r\n readonly tags?: EntityCardTag[];\r\n /** Click handler for entire card */\r\n readonly onClick?: () => void;\r\n /** Additional className */\r\n readonly className?: string;\r\n /** Custom header content (replaces default header) */\r\n readonly customHeader?: ReactNode;\r\n /** Custom body content (replaces default body) */\r\n readonly customBody?: ReactNode;\r\n /** Custom footer content (replaces default footer) */\r\n readonly customFooter?: ReactNode;\r\n}\r\n\r\n// ============================================================================\r\n// Helper Functions\r\n// ============================================================================\r\n\r\nconst tagVariantStyles: Record<string, string> = {\r\n default: 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]',\r\n primary: 'bg-[var(--accent-bg)] text-[var(--color-primary-600)]',\r\n success: 'bg-[var(--success-bg)] text-[var(--success-text)]',\r\n warning: 'bg-[var(--warning-bg)] text-[var(--warning-text)]',\r\n error: 'bg-[var(--error-bg)] text-[var(--error-text)]',\r\n info: 'bg-[var(--info-bg)] text-[var(--info-text)]',\r\n};\r\n\r\n// ============================================================================\r\n// Component\r\n// ============================================================================\r\n\r\nexport function EntityCard({\r\n avatar,\r\n title,\r\n subtitle,\r\n description,\r\n stats,\r\n badge,\r\n links,\r\n actions,\r\n tags,\r\n onClick,\r\n className = '',\r\n customHeader,\r\n customBody,\r\n customFooter,\r\n}: EntityCardProps): ReactElement | null {\r\n const cardClasses = [\r\n 'bg-[var(--bg-card)]',\r\n 'border-2 border-[var(--color-accent-200)] dark:border-[var(--color-accent-800)]',\r\n 'rounded-[var(--radius-card)]',\r\n 'overflow-hidden',\r\n 'transition-all',\r\n 'hover:shadow-lg hover:border-[var(--color-accent-400)]',\r\n 'flex flex-col',\r\n onClick ? 'cursor-pointer' : '',\r\n className,\r\n ].filter(Boolean).join(' ');\r\n\r\n // ============================================================================\r\n // Render Avatar\r\n // ============================================================================\r\n const renderAvatar = () => {\r\n if (!avatar) return null;\r\n\r\n if (avatar.imageUrl) {\r\n return (\r\n <div className=\"w-10 h-10 rounded-lg overflow-hidden flex-shrink-0 shadow-lg\">\r\n <img src={avatar.imageUrl} alt={title} className=\"w-full h-full object-cover\" />\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div\r\n className=\"w-10 h-10 rounded-lg flex items-center justify-center text-white font-bold shadow-lg flex-shrink-0\"\r\n style={{ backgroundColor: avatar.color }}\r\n >\r\n {avatar.letter}\r\n </div>\r\n );\r\n };\r\n\r\n // ============================================================================\r\n // Render Badge (in header)\r\n // ============================================================================\r\n const renderBadge = () => {\r\n if (!badge) return null;\r\n\r\n const BadgeIcon = badge.icon;\r\n const style = badge.color ? { color: badge.color } : {};\r\n\r\n return (\r\n <div className=\"group relative\">\r\n {BadgeIcon && (\r\n <BadgeIcon\r\n className=\"w-5 h-5 text-[var(--color-accent-700)] dark:text-[var(--color-accent-400)]\"\r\n style={style}\r\n />\r\n )}\r\n {badge.tooltip && (\r\n <div className=\"absolute top-full right-0 mt-1 px-2 py-1 bg-[var(--bg-primary)] border border-[var(--border-color)] rounded text-xs text-[var(--text-secondary)] whitespace-nowrap opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all z-10\">\r\n {badge.tooltip}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n };\r\n\r\n // ============================================================================\r\n // Render Header (colored section)\r\n // ============================================================================\r\n const renderHeader = () => {\r\n if (customHeader) return customHeader;\r\n\r\n return (\r\n <div className=\"bg-[var(--color-accent-50)] dark:bg-[var(--color-accent-900)]/20 p-4 border-b border-[var(--color-accent-200)] dark:border-[var(--color-accent-800)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n {renderAvatar()}\r\n <div>\r\n <h4 className=\"font-semibold text-[var(--text-primary)]\">{title}</h4>\r\n {subtitle && (\r\n <code className=\"text-xs text-[var(--text-secondary)]\">{subtitle}</code>\r\n )}\r\n </div>\r\n </div>\r\n {renderBadge()}\r\n </div>\r\n </div>\r\n );\r\n };\r\n\r\n // ============================================================================\r\n // Render Body\r\n // ============================================================================\r\n const renderBody = () => {\r\n if (customBody) return customBody;\r\n\r\n return (\r\n <div className=\"p-4 flex flex-col flex-1\">\r\n {/* Description */}\r\n {description && (\r\n <div className=\"text-sm text-[var(--text-secondary)] line-clamp-2\">\r\n {typeof description === 'string' ? <p>{description}</p> : description}\r\n </div>\r\n )}\r\n\r\n {/* Stats */}\r\n {stats && (\r\n <div className=\"text-sm text-[var(--text-secondary)] mt-3\">\r\n {typeof stats === 'string' ? (\r\n <>\r\n <span className=\"font-medium text-[var(--text-primary)]\">\r\n {stats.split(' ')[0]}\r\n </span>{' '}\r\n {stats.split(' ').slice(1).join(' ')}\r\n </>\r\n ) : (\r\n stats\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Tags */}\r\n {(tags?.length ?? 0) > 0 && (\r\n <div className=\"flex flex-wrap gap-2 mt-3\">\r\n {tags?.map((tag) =>\r\n tag.onClick ? (\r\n <button\r\n type=\"button\"\r\n key={tag.label}\r\n className={`text-xs px-2 py-1 rounded-full cursor-pointer hover:opacity-80 ${tagVariantStyles[tag.variant || 'default']}`}\r\n onClick={tag.onClick}\r\n >\r\n {tag.label}\r\n </button>\r\n ) : (\r\n <span\r\n key={tag.label}\r\n className={`text-xs px-2 py-1 rounded-full ${tagVariantStyles[tag.variant || 'default']}`}\r\n >\r\n {tag.label}\r\n </span>\r\n )\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Links */}\r\n {(links?.length ?? 0) > 0 && (\r\n <div className=\"space-y-1.5 pt-2 mt-3 border-t border-[var(--border-color)]\">\r\n {links?.map((link) => {\r\n const LinkIcon = link.icon;\r\n if (link.href) {\r\n return (\r\n <a\r\n key={link.label}\r\n href={link.href}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"flex items-center gap-2 text-sm text-[var(--text-secondary)] hover:text-[var(--color-accent-600)] transition-colors\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <LinkIcon className=\"w-3.5 h-3.5\" />\r\n {link.label}\r\n </a>\r\n );\r\n }\r\n return (\r\n <button\r\n key={link.label}\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n link.onClick?.();\r\n }}\r\n className=\"flex items-center gap-2 text-sm text-[var(--text-secondary)] hover:text-[var(--color-accent-600)] transition-colors\"\r\n >\r\n <LinkIcon className=\"w-3.5 h-3.5\" />\r\n {link.label}\r\n </button>\r\n );\r\n })}\r\n </div>\r\n )}\r\n\r\n {/* Spacer to push actions to bottom */}\r\n <div className=\"flex-1 min-h-4\" />\r\n\r\n {/* Actions */}\r\n {renderFooter()}\r\n </div>\r\n );\r\n };\r\n\r\n // ============================================================================\r\n // Render Footer (Actions)\r\n // ============================================================================\r\n const renderFooter = () => {\r\n if (customFooter) return customFooter;\r\n if (!actions || actions.length === 0) return null;\r\n\r\n return (\r\n <div className=\"space-y-2 mt-3\">\r\n {actions.map((action) => {\r\n const ActionIcon = action.icon;\r\n const variant = action.variant || 'primary';\r\n\r\n // Base classes\r\n const baseClasses = 'btn btn-sm flex items-center justify-center gap-1 w-full';\r\n\r\n // Variant classes\r\n const variantClasses = {\r\n primary: 'bg-[var(--color-accent-500)] hover:bg-[var(--color-accent-600)] text-white',\r\n secondary: 'bg-[var(--color-accent-700)] hover:bg-[var(--color-accent-800)] text-white',\r\n ghost: 'bg-transparent border border-[var(--border-color)] text-[var(--text-primary)] hover:bg-[var(--bg-secondary)]',\r\n };\r\n\r\n const className = `${baseClasses} ${variantClasses[variant]} ${action.disabled ? 'opacity-50 cursor-not-allowed' : ''}`;\r\n\r\n // Loading spinner\r\n const loadingSpinner = action.loading && (\r\n <span className=\"w-4 h-4 border-2 border-current border-t-transparent rounded-full animate-spin\" />\r\n );\r\n\r\n // Icon\r\n const iconElement = !action.loading && ActionIcon && <ActionIcon className=\"w-4 h-4\" />;\r\n\r\n // If href, render as <a>\r\n if (action.href) {\r\n return (\r\n <a\r\n key={action.label}\r\n href={action.href}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className={className}\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n {loadingSpinner}\r\n {iconElement}\r\n {action.label}\r\n </a>\r\n );\r\n }\r\n\r\n // Otherwise render as <button>\r\n return (\r\n <button\r\n key={action.label}\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n action.onClick?.();\r\n }}\r\n disabled={action.disabled || action.loading}\r\n className={className}\r\n >\r\n {loadingSpinner}\r\n {iconElement}\r\n {action.label}\r\n </button>\r\n );\r\n })}\r\n </div>\r\n );\r\n };\r\n\r\n // ============================================================================\r\n // Main Render\r\n // ============================================================================\r\n if (onClick) {\r\n return (\r\n <button\r\n type=\"button\"\r\n className={`${cardClasses} text-left w-full`}\r\n onClick={onClick}\r\n >\r\n {renderHeader()}\r\n {renderBody()}\r\n </button>\r\n );\r\n }\r\n\r\n return (\r\n <div className={cardClasses}>\r\n {renderHeader()}\r\n {renderBody()}\r\n </div>\r\n );\r\n}\r\n\r\n// ============================================================================\r\n// Preset Variants\r\n// ============================================================================\r\n\r\n/**\r\n * ProviderCard - Preset for AI provider cards\r\n * Matches the design in /administration/ai/settings\r\n */\r\nexport interface ProviderCardProps {\r\n readonly name: string;\r\n readonly code: string;\r\n readonly description: string;\r\n readonly modelCount: number;\r\n readonly color: string;\r\n readonly websiteUrl?: string;\r\n readonly docsUrl?: string;\r\n readonly apiKeyUrl?: string;\r\n readonly adminApiKeyUrl?: string;\r\n readonly hasAdminKey?: boolean;\r\n readonly badgeIcon?: ElementType;\r\n readonly badgeTooltip?: string;\r\n readonly onGetApiKey?: () => void;\r\n readonly onGetAdminApiKey?: () => void;\r\n /** i18n labels */\r\n readonly labels?: {\r\n readonly websiteLabel?: string;\r\n readonly docsLabel?: string;\r\n readonly apiKeyLabel?: string;\r\n readonly adminApiKeyLabel?: string;\r\n readonly modelsLabel?: string;\r\n };\r\n}\r\n\r\nexport function ProviderCard({\r\n name,\r\n code,\r\n description,\r\n modelCount,\r\n color,\r\n websiteUrl,\r\n docsUrl,\r\n apiKeyUrl,\r\n adminApiKeyUrl,\r\n hasAdminKey = false,\r\n badgeIcon,\r\n badgeTooltip,\r\n onGetApiKey,\r\n onGetAdminApiKey,\r\n labels = {},\r\n}: ProviderCardProps): ReactElement {\r\n const {\r\n websiteLabel = 'Site officiel',\r\n docsLabel = 'Documentation API',\r\n apiKeyLabel = 'Obtenir une clé API',\r\n adminApiKeyLabel = 'Obtenir une clé API Admin',\r\n modelsLabel = 'modèle(s)',\r\n } = labels;\r\n\r\n const links: EntityCardLink[] = [];\r\n if (websiteUrl) {\r\n links.push({ icon: ExternalLink, label: websiteLabel, href: websiteUrl });\r\n }\r\n if (docsUrl) {\r\n links.push({ icon: BookOpen, label: docsLabel, href: docsUrl });\r\n }\r\n\r\n const actions: EntityCardAction[] = [];\r\n\r\n // API Key action (href or onClick)\r\n if (apiKeyUrl) {\r\n actions.push({\r\n label: apiKeyLabel,\r\n href: apiKeyUrl,\r\n variant: 'primary',\r\n });\r\n } else if (onGetApiKey) {\r\n actions.push({\r\n label: apiKeyLabel,\r\n onClick: onGetApiKey,\r\n variant: 'primary',\r\n });\r\n }\r\n\r\n // Admin API Key action\r\n if (hasAdminKey) {\r\n if (adminApiKeyUrl) {\r\n actions.push({\r\n label: adminApiKeyLabel,\r\n href: adminApiKeyUrl,\r\n variant: 'secondary',\r\n });\r\n } else if (onGetAdminApiKey) {\r\n actions.push({\r\n label: adminApiKeyLabel,\r\n onClick: onGetAdminApiKey,\r\n variant: 'secondary',\r\n });\r\n }\r\n }\r\n\r\n return (\r\n <EntityCard\r\n avatar={{ letter: name.at(0)?.toUpperCase() ?? '', color }}\r\n title={name}\r\n subtitle={code}\r\n description={description}\r\n stats={`${modelCount} ${modelsLabel}`}\r\n badge={badgeIcon ? { icon: badgeIcon, tooltip: badgeTooltip } : undefined}\r\n links={links}\r\n actions={actions}\r\n />\r\n );\r\n}\r\n\r\n/**\r\n * TemplateCard - Preset for email/workflow templates\r\n */\r\nexport interface TemplateCardProps {\r\n readonly name: string;\r\n readonly code: string;\r\n readonly category: string;\r\n readonly isActive?: boolean;\r\n readonly isSystem?: boolean;\r\n readonly icon?: ElementType;\r\n readonly iconColor?: string;\r\n readonly translationsCount?: number;\r\n readonly onEdit?: () => void;\r\n readonly onDelete?: () => void;\r\n readonly onClick?: () => void;\r\n /** i18n labels */\r\n readonly labels?: {\r\n readonly systemLabel?: string;\r\n readonly inactiveLabel?: string;\r\n readonly activeLabel?: string;\r\n readonly editLabel?: string;\r\n readonly deleteLabel?: string;\r\n };\r\n}\r\n\r\nexport function TemplateCard({\r\n name,\r\n code,\r\n category,\r\n isActive = true,\r\n isSystem = false,\r\n icon: IconComponent,\r\n iconColor = 'var(--color-accent-500)',\r\n translationsCount,\r\n onEdit,\r\n onDelete,\r\n onClick,\r\n labels = {},\r\n}: TemplateCardProps): ReactElement {\r\n const {\r\n systemLabel = 'System',\r\n inactiveLabel = 'Inactif',\r\n activeLabel = 'Actif',\r\n editLabel = 'Modifier',\r\n deleteLabel = 'Supprimer',\r\n } = labels;\r\n\r\n const tags: EntityCardTag[] = [\r\n { label: category, variant: 'default' },\r\n ];\r\n if (isSystem) {\r\n tags.push({ label: systemLabel, variant: 'info' });\r\n }\r\n\r\n // Build actions array\r\n const actions: EntityCardAction[] = [];\r\n if (onEdit) {\r\n actions.push({ label: editLabel, onClick: onEdit, variant: 'ghost' });\r\n }\r\n if (onDelete && !isSystem) {\r\n actions.push({ label: deleteLabel, onClick: onDelete, variant: 'ghost' });\r\n }\r\n\r\n // Custom header with icon, status badge, and translations count\r\n const customHeader = (\r\n <div className=\"bg-[var(--color-accent-50)] dark:bg-[var(--color-accent-900)]/20 p-4 border-b border-[var(--color-accent-200)] dark:border-[var(--color-accent-800)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n {IconComponent ? (\r\n <div\r\n className=\"w-10 h-10 rounded-lg flex items-center justify-center shadow-lg\"\r\n style={{ backgroundColor: iconColor }}\r\n >\r\n <IconComponent className=\"w-5 h-5 text-white\" />\r\n </div>\r\n ) : (\r\n <div\r\n className=\"w-10 h-10 rounded-lg flex items-center justify-center text-white font-bold shadow-lg\"\r\n style={{ backgroundColor: iconColor }}\r\n >\r\n {name.at(0)?.toUpperCase()}\r\n </div>\r\n )}\r\n <div>\r\n <h4 className=\"font-semibold text-[var(--text-primary)]\">{name}</h4>\r\n <code className=\"text-xs text-[var(--text-secondary)]\">{code}</code>\r\n </div>\r\n </div>\r\n {/* Status badge */}\r\n <span className={`inline-flex items-center gap-1.5 px-2 py-0.5 text-xs rounded-full font-medium ${\r\n isActive\r\n ? 'bg-[var(--success-bg)] text-[var(--success-text)]'\r\n : 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]'\r\n }`}>\r\n <span className={`w-1.5 h-1.5 rounded-full ${isActive ? 'bg-[var(--success-dot)]' : 'bg-[var(--text-muted)]'}`} />\r\n {isActive ? activeLabel : inactiveLabel}\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n\r\n // Custom body with tags and translations count\r\n const customBody = (\r\n <div className=\"p-4 flex flex-col flex-1\">\r\n {/* Tags */}\r\n <div className=\"flex flex-wrap gap-2\">\r\n {tags.map((tag) => (\r\n <span\r\n key={tag.label}\r\n className={`text-xs px-2 py-1 rounded-full ${\r\n tag.variant === 'info'\r\n ? 'bg-[var(--info-bg)] text-[var(--info-text)]'\r\n : 'bg-[var(--bg-secondary)] text-[var(--text-secondary)]'\r\n }`}\r\n >\r\n {tag.label}\r\n </span>\r\n ))}\r\n </div>\r\n\r\n {/* Translations count */}\r\n {translationsCount !== undefined && (\r\n <div className=\"flex items-center gap-1.5 mt-3 text-[var(--text-tertiary)]\">\r\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\r\n <path d=\"m5 8 6 6\" /><path d=\"m4 14 6-6 2-3\" /><path d=\"M2 5h12\" /><path d=\"M7 2h1\" /><path d=\"m22 22-5-10-5 10\" /><path d=\"M14 18h6\" />\r\n </svg>\r\n <span className=\"text-xs\">{translationsCount}</span>\r\n </div>\r\n )}\r\n\r\n {/* Spacer */}\r\n <div className=\"flex-1 min-h-4\" />\r\n\r\n {/* Actions */}\r\n {actions.length > 0 && (\r\n <div className=\"flex items-center justify-end gap-1 mt-3 pt-3 border-t border-[var(--border-color)]\">\r\n {actions.map((action) => (\r\n <button\r\n key={action.label}\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n action.onClick?.();\r\n }}\r\n className=\"p-1.5 rounded-lg text-[var(--text-tertiary)] hover:text-[var(--color-accent-600)] hover:bg-[var(--color-accent-50)] transition-all\"\r\n title={action.label}\r\n >\r\n {action.label === editLabel ? (\r\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\r\n <path d=\"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\" />\r\n </svg>\r\n ) : (\r\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\r\n <path d=\"M3 6h18\" /><path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\" /><path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\" />\r\n </svg>\r\n )}\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n\r\n return (\r\n <EntityCard\r\n title={name}\r\n subtitle={code}\r\n onClick={onClick}\r\n customHeader={customHeader}\r\n customBody={customBody}\r\n />\r\n );\r\n}\r\n\r\nexport default EntityCard;\r\n","import { useState, useCallback, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { Plus, MoreHorizontal, GripVertical, Loader2 } from 'lucide-react';\r\n\r\n/**\r\n * KanbanBoard - Composant Kanban drag-and-drop standardisé SmartStack\r\n *\r\n * Fonctionnalités:\r\n * - Colonnes avec compteurs\r\n * - Drag & drop des cartes (natif HTML5)\r\n * - Actions sur cartes et colonnes\r\n * - États vide/chargement\r\n * - Personnalisation des cartes\r\n *\r\n * Usage:\r\n * ```tsx\r\n * <KanbanBoard\r\n * columns={[\r\n * { id: 'todo', title: 'À faire', color: 'var(--warning-text)' },\r\n * { id: 'in-progress', title: 'En cours', color: 'var(--info-text)' },\r\n * { id: 'done', title: 'Terminé', color: 'var(--success-text)' },\r\n * ]}\r\n * items={tasks}\r\n * getItemColumn={(task) => task.status}\r\n * onMoveItem={(taskId, newColumnId) => updateTask(taskId, { status: newColumnId })}\r\n * renderCard={(task) => <TaskCard task={task} />}\r\n * />\r\n * ```\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface KanbanColumn {\r\n /** Unique column ID */\r\n id: string;\r\n /** Column title */\r\n title: string;\r\n /** Optional color (CSS value) */\r\n color?: string;\r\n /** Max items in column (optional limit) */\r\n maxItems?: number;\r\n /** Can add items to this column */\r\n canAddItem?: boolean;\r\n}\r\n\r\nexport interface KanbanBoardProps<T> {\r\n /** Column definitions */\r\n readonly columns: KanbanColumn[];\r\n /** All items */\r\n readonly items: T[];\r\n /** Get unique item ID */\r\n readonly getItemId: (item: T) => string;\r\n /** Get item's column ID */\r\n readonly getItemColumn: (item: T) => string;\r\n /** Move item to new column */\r\n readonly onMoveItem: (itemId: string, newColumnId: string, newIndex?: number) => void;\r\n /** Render card content */\r\n readonly renderCard: (item: T, index: number) => ReactNode;\r\n /** Loading state */\r\n readonly loading?: boolean;\r\n /** Add item handler */\r\n readonly onAddItem?: (columnId: string) => void;\r\n /** Card click handler */\r\n readonly onCardClick?: (item: T) => void;\r\n /** Card menu actions */\r\n readonly cardActions?: (item: T) => { label: string; onClick: () => void; icon?: ReactNode }[];\r\n /** Empty column message */\r\n readonly emptyColumnMessage?: string;\r\n /** Custom className */\r\n readonly className?: string;\r\n /** Column min width */\r\n readonly columnMinWidth?: string;\r\n /** Disable drag & drop */\r\n readonly disabled?: boolean;\r\n}\r\n\r\n// ============================================================================\r\n// Component\r\n// ============================================================================\r\n\r\nexport function KanbanBoard<T>({\r\n columns,\r\n items,\r\n getItemId,\r\n getItemColumn,\r\n onMoveItem,\r\n renderCard,\r\n loading = false,\r\n onAddItem,\r\n onCardClick,\r\n cardActions,\r\n emptyColumnMessage = 'Aucun élément',\r\n className = '',\r\n columnMinWidth = '280px',\r\n disabled = false,\r\n}: KanbanBoardProps<T>): ReactElement {\r\n const [draggedItemId, setDraggedItemId] = useState<string | null>(null);\r\n const [dragOverColumnId, setDragOverColumnId] = useState<string | null>(null);\r\n const [activeCardMenu, setActiveCardMenu] = useState<string | null>(null);\r\n\r\n const handleActionClick = useCallback((action: { onClick: () => void }) => {\r\n action.onClick();\r\n setActiveCardMenu(null);\r\n }, []);\r\n\r\n const renderActionMenuItem = useCallback((action: { label: string; onClick: () => void; icon?: ReactNode }, actionIndex: number) => (\r\n <button\r\n key={actionIndex}\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n handleActionClick(action);\r\n }}\r\n className=\"w-full flex items-center gap-2 px-3 py-2 text-sm text-left hover:bg-[var(--bg-tertiary)]\"\r\n >\r\n {action.icon}\r\n {action.label}\r\n </button>\r\n ), [handleActionClick]);\r\n\r\n // Get items for a column\r\n const getColumnItems = useCallback(\r\n (columnId: string) => items.filter((item) => getItemColumn(item) === columnId),\r\n [items, getItemColumn]\r\n );\r\n\r\n // Drag handlers\r\n const handleDragStart = (e: React.DragEvent, itemId: string) => {\r\n if (disabled) return;\r\n setDraggedItemId(itemId);\r\n e.dataTransfer.effectAllowed = 'move';\r\n e.dataTransfer.setData('text/plain', itemId);\r\n };\r\n\r\n const handleDragOver = (e: React.DragEvent, columnId: string) => {\r\n if (disabled || !draggedItemId) return;\r\n e.preventDefault();\r\n e.dataTransfer.dropEffect = 'move';\r\n setDragOverColumnId(columnId);\r\n };\r\n\r\n const handleDragLeave = () => {\r\n setDragOverColumnId(null);\r\n };\r\n\r\n const handleDrop = (e: React.DragEvent, columnId: string) => {\r\n if (disabled) return;\r\n e.preventDefault();\r\n const itemId = e.dataTransfer.getData('text/plain');\r\n if (itemId) {\r\n onMoveItem(itemId, columnId);\r\n }\r\n setDraggedItemId(null);\r\n setDragOverColumnId(null);\r\n };\r\n\r\n const handleDragEnd = () => {\r\n setDraggedItemId(null);\r\n setDragOverColumnId(null);\r\n };\r\n\r\n if (loading) {\r\n return (\r\n <div className=\"flex items-center justify-center py-12\">\r\n <Loader2 className=\"w-8 h-8 animate-spin text-[var(--color-primary-500)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className={`flex gap-4 overflow-x-auto pb-4 ${className}`}>\r\n {columns.map((column) => {\r\n const columnItems = getColumnItems(column.id);\r\n const isDragOver = dragOverColumnId === column.id;\r\n const isOverLimit = column.maxItems !== undefined && columnItems.length >= column.maxItems;\r\n\r\n return (\r\n <div\r\n key={column.id}\r\n className={`flex-shrink-0 flex flex-col rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)] ${\r\n isDragOver ? 'ring-2 ring-[var(--color-primary-500)]' : ''\r\n }`}\r\n style={{ minWidth: columnMinWidth, maxWidth: columnMinWidth }}\r\n onDragOver={(e) => handleDragOver(e, column.id)}\r\n onDragLeave={handleDragLeave}\r\n onDrop={(e) => handleDrop(e, column.id)}\r\n >\r\n {/* Column Header */}\r\n <div className=\"flex items-center justify-between p-3 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-2\">\r\n {column.color && (\r\n <div\r\n className=\"w-3 h-3 rounded-full\"\r\n style={{ backgroundColor: column.color }}\r\n />\r\n )}\r\n <h3 className=\"font-semibold text-sm text-[var(--text-primary)]\">\r\n {column.title}\r\n </h3>\r\n <span className=\"text-xs text-[var(--text-secondary)] px-1.5 py-0.5 rounded-full bg-[var(--bg-tertiary)]\">\r\n {columnItems.length}\r\n {column.maxItems !== undefined && ` / ${column.maxItems}`}\r\n </span>\r\n </div>\r\n {onAddItem && column.canAddItem !== false && !isOverLimit && (\r\n <button\r\n onClick={() => onAddItem(column.id)}\r\n className=\"btn btn-ghost p-1 hover:bg-[var(--bg-tertiary)]\"\r\n title=\"Ajouter\"\r\n aria-label={`Ajouter à ${column.title}`}\r\n >\r\n <Plus className=\"w-4 h-4\" />\r\n </button>\r\n )}\r\n </div>\r\n\r\n {/* Column Content */}\r\n <div className=\"flex-1 p-2 space-y-2 overflow-y-auto max-h-[calc(100vh-300px)]\">\r\n {columnItems.length === 0 ? (\r\n <div className=\"text-center py-8 text-[var(--text-secondary)] text-sm\">\r\n {emptyColumnMessage}\r\n </div>\r\n ) : (\r\n columnItems.map((item, index) => {\r\n const itemId = getItemId(item);\r\n const isDragging = draggedItemId === itemId;\r\n const actions = cardActions?.(item);\r\n\r\n return (\r\n <div\r\n key={itemId}\r\n draggable={!disabled}\r\n onDragStart={(e) => handleDragStart(e, itemId)}\r\n onDragEnd={handleDragEnd}\r\n onClick={() => onCardClick?.(item)}\r\n onKeyDown={onCardClick ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onCardClick(item); } } : undefined}\r\n role={onCardClick ? 'button' : undefined}\r\n tabIndex={onCardClick ? 0 : undefined}\r\n className={`\r\n relative group\r\n bg-[var(--bg-card)] border border-[var(--border-color)] rounded-lg\r\n ${!disabled ? 'cursor-grab active:cursor-grabbing' : ''}\r\n ${isDragging ? 'opacity-50 ring-2 ring-[var(--color-primary-500)]' : ''}\r\n ${onCardClick ? 'hover:border-[var(--color-primary-500)]' : ''}\r\n transition-all\r\n `}\r\n >\r\n {/* Drag Handle */}\r\n {!disabled && (\r\n <div className=\"absolute left-0 top-0 bottom-0 w-6 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity\">\r\n <GripVertical className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n </div>\r\n )}\r\n\r\n {/* Card Content */}\r\n <div className={`p-3 ${!disabled ? 'pl-6' : ''}`}>\r\n {renderCard(item, index)}\r\n </div>\r\n\r\n {/* Card Actions Menu */}\r\n {(actions?.length ?? 0) > 0 && (\r\n <div className=\"absolute top-2 right-2\">\r\n <button\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n setActiveCardMenu(activeCardMenu === itemId ? null : itemId);\r\n }}\r\n className=\"btn btn-ghost p-1 opacity-0 group-hover:opacity-100 hover:bg-[var(--bg-tertiary)]\"\r\n aria-label=\"Actions\"\r\n >\r\n <MoreHorizontal className=\"w-4 h-4\" />\r\n </button>\r\n\r\n {activeCardMenu === itemId && (\r\n <>\r\n <button\r\n type=\"button\"\r\n className=\"fixed inset-0 z-40\"\r\n onClick={() => setActiveCardMenu(null)}\r\n aria-label=\"Close menu\"\r\n />\r\n <div className=\"absolute right-0 top-full mt-1 z-50 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-lg shadow-lg py-1 min-w-[150px]\">\r\n {actions?.map((action, actionIndex) => renderActionMenuItem(action, actionIndex))}\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n })\r\n )}\r\n\r\n {/* Drop zone indicator */}\r\n {isDragOver && columnItems.length > 0 && (\r\n <div className=\"h-16 border-2 border-dashed border-[var(--color-primary-500)] rounded-lg bg-[var(--accent-bg)] flex items-center justify-center\">\r\n <span className=\"text-sm text-[var(--color-primary-500)]\">Déposer ici</span>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n );\r\n}\r\n\r\n// ============================================================================\r\n// Preset Card Components\r\n// ============================================================================\r\n\r\nexport interface KanbanCardProps {\r\n readonly title: string;\r\n readonly subtitle?: string;\r\n readonly tags?: { label: string; color?: string }[];\r\n readonly avatar?: { letter: string; color: string } | { imageUrl: string };\r\n readonly priority?: 'low' | 'medium' | 'high' | 'critical';\r\n readonly dueDate?: string;\r\n}\r\n\r\nconst priorityColors = {\r\n low: 'var(--info-text)',\r\n medium: 'var(--warning-text)',\r\n high: 'var(--error-text)',\r\n critical: 'var(--error-text)',\r\n};\r\n\r\nexport function KanbanCard({ title, subtitle, tags, avatar, priority, dueDate }: KanbanCardProps): ReactElement {\r\n return (\r\n <div className=\"space-y-2\">\r\n {/* Header with avatar */}\r\n {avatar && (\r\n <div className=\"flex items-center gap-2\">\r\n {'imageUrl' in avatar ? (\r\n <img src={avatar.imageUrl} alt=\"\" className=\"w-6 h-6 rounded-full\" />\r\n ) : (\r\n <div\r\n className=\"w-6 h-6 rounded-full flex items-center justify-center text-white text-xs font-medium\"\r\n style={{ backgroundColor: avatar.color }}\r\n >\r\n {avatar.letter}\r\n </div>\r\n )}\r\n {subtitle && <span className=\"text-xs text-[var(--text-secondary)]\">{subtitle}</span>}\r\n </div>\r\n )}\r\n\r\n {/* Title */}\r\n <h4 className=\"text-sm font-medium text-[var(--text-primary)] line-clamp-2\">{title}</h4>\r\n\r\n {/* Tags */}\r\n {(tags?.length ?? 0) > 0 && (\r\n <div className=\"flex flex-wrap gap-1\">\r\n {tags?.map((tag) => (\r\n <span\r\n key={tag.label}\r\n className=\"text-xs px-1.5 py-0.5 rounded\"\r\n style={{\r\n backgroundColor: tag.color ? `${tag.color}20` : 'var(--bg-tertiary)',\r\n color: tag.color || 'var(--text-secondary)',\r\n }}\r\n >\r\n {tag.label}\r\n </span>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Footer with priority and due date */}\r\n {(priority || dueDate) && (\r\n <div className=\"flex items-center justify-between text-xs\">\r\n {priority && (\r\n <span\r\n className=\"px-1.5 py-0.5 rounded font-medium\"\r\n style={{\r\n backgroundColor: `${priorityColors[priority]}20`,\r\n color: priorityColors[priority],\r\n }}\r\n >\r\n {priority.charAt(0).toUpperCase() + priority.slice(1)}\r\n </span>\r\n )}\r\n {dueDate && (\r\n <span className=\"text-[var(--text-secondary)]\">\r\n {new Date(dueDate).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default KanbanBoard;\r\n","import type { ReactNode, ReactElement } from 'react';\r\n\r\ninterface PageHeaderProps {\r\n readonly title: string;\r\n readonly subtitle?: string;\r\n readonly icon?: ReactNode;\r\n readonly actions?: ReactNode;\r\n}\r\n\r\nexport function PageHeader({\r\n title,\r\n subtitle,\r\n icon,\r\n actions,\r\n}: PageHeaderProps): ReactElement {\r\n return (\r\n <div className=\"flex items-center justify-between\">\r\n <div>\r\n <h1 className=\"text-2xl font-bold flex items-center gap-2\">\r\n {icon}\r\n {title}\r\n </h1>\r\n {subtitle && (\r\n <p className=\"text-[var(--text-secondary)]\">{subtitle}</p>\r\n )}\r\n </div>\r\n {actions && (\r\n <div className=\"flex items-center gap-3\">\r\n {actions}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n","import { useState, useMemo, useCallback, type ReactNode } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport {\r\n ChevronRight, ChevronDown, Check, X, Minus,\r\n Loader2, Lock, Search\r\n} from 'lucide-react';\r\n\r\n/**\r\n * PermissionMatrix - Composant de matrice de permissions standardisé SmartStack\r\n *\r\n * Fonctionnalités:\r\n * - Grille rows × columns avec états\r\n * - Groupes de lignes (collapsibles)\r\n * - En-têtes de colonnes rotatifs\r\n * - États: granted, denied, partial, inherited\r\n * - Click pour toggle\r\n * - Légende automatique\r\n *\r\n * Usage:\r\n * ```tsx\r\n * <PermissionMatrix\r\n * rows={resources}\r\n * columns={roles}\r\n * getRowId={(r) => r.id}\r\n * getRowLabel={(r) => r.name}\r\n * getRowGroup={(r) => r.category}\r\n * getColumnId={(c) => c.id}\r\n * getColumnLabel={(c) => c.name}\r\n * getCellState={(rowId, colId) => assignments[rowId]?.[colId]}\r\n * onCellClick={(rowId, colId) => toggleAssignment(rowId, colId)}\r\n * />\r\n * ```\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport type CellState = 'granted' | 'denied' | 'partial' | 'inherited' | 'none';\r\n\r\nexport interface PermissionMatrixRow {\r\n id: string;\r\n label: string;\r\n group?: string;\r\n subGroup?: string;\r\n icon?: ReactNode;\r\n locked?: boolean;\r\n childCount?: number;\r\n}\r\n\r\nexport interface PermissionMatrixColumn {\r\n id: string;\r\n label: string;\r\n shortLabel?: string;\r\n locked?: boolean;\r\n color?: string;\r\n}\r\n\r\nexport interface PermissionMatrixProps<R, C> {\r\n /** Row data array */\r\n rows: R[];\r\n /** Column data array */\r\n columns: C[];\r\n /** Get unique row ID */\r\n getRowId: (row: R) => string;\r\n /** Get row display label */\r\n getRowLabel: (row: R) => string;\r\n /** Get row group (for collapsible sections) */\r\n getRowGroup?: (row: R) => string | undefined;\r\n /** Get row sub-group */\r\n getRowSubGroup?: (row: R) => string | undefined;\r\n /** Get row icon */\r\n getRowIcon?: (row: R) => ReactNode;\r\n /** Is row locked (read-only) */\r\n isRowLocked?: (row: R) => boolean;\r\n /** Get row child count (for display) */\r\n getRowChildCount?: (row: R) => number | undefined;\r\n /** Get unique column ID */\r\n getColumnId: (col: C) => string;\r\n /** Get column display label */\r\n getColumnLabel: (col: C) => string;\r\n /** Get column short label (for rotated header) */\r\n getColumnShortLabel?: (col: C) => string;\r\n /** Is column locked (read-only) */\r\n isColumnLocked?: (col: C) => boolean;\r\n /** Get column color */\r\n getColumnColor?: (col: C) => string | undefined;\r\n /** Get cell state */\r\n getCellState: (rowId: string, colId: string) => CellState;\r\n /** Cell click handler */\r\n onCellClick?: (rowId: string, colId: string) => void;\r\n /** Loading state */\r\n loading?: boolean;\r\n /** Show legend */\r\n showLegend?: boolean;\r\n /** Show search */\r\n searchable?: boolean;\r\n /** Row column width */\r\n rowColumnWidth?: string;\r\n /** Cell width */\r\n cellWidth?: string;\r\n /** Custom className */\r\n className?: string;\r\n /** Empty message */\r\n emptyMessage?: string;\r\n /** Can edit locked columns */\r\n canEditLocked?: boolean;\r\n}\r\n\r\n// ============================================================================\r\n// Helper Functions\r\n// ============================================================================\r\n\r\nconst cellStateConfig: Record<CellState, { icon: typeof Check; color: string; bg: string; label: string }> = {\r\n granted: {\r\n icon: Check,\r\n color: 'var(--success-text)',\r\n bg: 'var(--success-bg)',\r\n label: 'Accordé',\r\n },\r\n denied: {\r\n icon: X,\r\n color: 'var(--error-text)',\r\n bg: 'var(--error-bg)',\r\n label: 'Refusé',\r\n },\r\n partial: {\r\n icon: Minus,\r\n color: 'var(--warning-text)',\r\n bg: 'var(--warning-bg)',\r\n label: 'Partiel',\r\n },\r\n inherited: {\r\n icon: Check,\r\n color: 'var(--info-text)',\r\n bg: 'var(--info-bg)',\r\n label: 'Hérité',\r\n },\r\n none: {\r\n icon: Minus,\r\n color: 'var(--text-tertiary)',\r\n bg: 'transparent',\r\n label: 'Non défini',\r\n },\r\n};\r\n\r\n// ============================================================================\r\n// Component\r\n// ============================================================================\r\n\r\nexport function PermissionMatrix<R, C>({\r\n rows,\r\n columns,\r\n getRowId,\r\n getRowLabel,\r\n getRowGroup,\r\n getRowSubGroup,\r\n getRowIcon,\r\n isRowLocked,\r\n getRowChildCount,\r\n getColumnId,\r\n getColumnLabel,\r\n getColumnShortLabel,\r\n isColumnLocked,\r\n getColumnColor,\r\n getCellState,\r\n onCellClick,\r\n loading = false,\r\n showLegend = true,\r\n searchable = false,\r\n rowColumnWidth = '250px',\r\n cellWidth = '44px',\r\n className = '',\r\n emptyMessage = 'Aucune donnée',\r\n canEditLocked = false,\r\n}: PermissionMatrixProps<R, C>): ReactElement {\r\n const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set());\r\n const [searchTerm, setSearchTerm] = useState('');\r\n\r\n // Group rows\r\n const groupedRows = useMemo(() => {\r\n const groups = new Map<string, { rows: R[]; subGroups: Map<string, R[]> }>();\r\n\r\n rows.forEach((row) => {\r\n const group = getRowGroup?.(row) || '_default';\r\n const subGroup = getRowSubGroup?.(row);\r\n\r\n if (!groups.has(group)) {\r\n groups.set(group, { rows: [], subGroups: new Map() });\r\n }\r\n\r\n const groupData = groups.get(group)!;\r\n\r\n if (subGroup) {\r\n if (!groupData.subGroups.has(subGroup)) {\r\n groupData.subGroups.set(subGroup, []);\r\n }\r\n groupData.subGroups.get(subGroup)!.push(row);\r\n } else {\r\n groupData.rows.push(row);\r\n }\r\n });\r\n\r\n return groups;\r\n }, [rows, getRowGroup, getRowSubGroup]);\r\n\r\n // Filter rows by search\r\n const filterSubGroupsBySearch = useCallback((subGroups: Map<string, R[]>, search: string) => {\r\n const matchingSubGroups = new Map<string, R[]>();\r\n subGroups.forEach((subRows, subGroup) => {\r\n const matching = subRows.filter((row) =>\r\n getRowLabel(row).toLowerCase().includes(search)\r\n );\r\n if (matching.length > 0) {\r\n matchingSubGroups.set(subGroup, matching);\r\n }\r\n });\r\n return matchingSubGroups;\r\n }, [getRowLabel]);\r\n\r\n const filteredGroups = useMemo(() => {\r\n if (!searchTerm) return groupedRows;\r\n\r\n const search = searchTerm.toLowerCase();\r\n const filtered = new Map<string, { rows: R[]; subGroups: Map<string, R[]> }>();\r\n\r\n groupedRows.forEach((groupData, group) => {\r\n const matchingRows = groupData.rows.filter((row) =>\r\n getRowLabel(row).toLowerCase().includes(search)\r\n );\r\n\r\n const matchingSubGroups = filterSubGroupsBySearch(groupData.subGroups, search);\r\n\r\n if (matchingRows.length > 0 || matchingSubGroups.size > 0) {\r\n filtered.set(group, { rows: matchingRows, subGroups: matchingSubGroups });\r\n }\r\n });\r\n\r\n return filtered;\r\n }, [groupedRows, searchTerm, getRowLabel, filterSubGroupsBySearch]);\r\n\r\n // Toggle group collapse\r\n const toggleGroup = (group: string) => {\r\n setCollapsedGroups((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(group)) {\r\n next.delete(group);\r\n } else {\r\n next.add(group);\r\n }\r\n return next;\r\n });\r\n };\r\n\r\n // Handle cell click\r\n const handleCellClick = useCallback((rowId: string, colId: string, rowLocked: boolean, colLocked: boolean) => {\r\n if (!onCellClick) return;\r\n if (rowLocked && !canEditLocked) return;\r\n if (colLocked && !canEditLocked) return;\r\n onCellClick(rowId, colId);\r\n }, [onCellClick, canEditLocked]);\r\n\r\n const isCellEditable = useCallback((rowLocked: boolean, colLocked: boolean): boolean => {\r\n if (!onCellClick) return false;\r\n if (rowLocked && !canEditLocked) return false;\r\n if (colLocked && !canEditLocked) return false;\r\n return true;\r\n }, [onCellClick, canEditLocked]);\r\n\r\n const getCellButtonClass = (isEditable: boolean, state: CellState): string => {\r\n const baseClass = 'w-full h-full min-h-[32px] flex items-center justify-center rounded transition-colors';\r\n const cursorClass = isEditable ? 'hover:opacity-80 cursor-pointer' : 'cursor-default';\r\n const opacityClass = state !== 'none' ? '' : 'opacity-30';\r\n return `${baseClass} ${cursorClass} ${opacityClass}`;\r\n };\r\n\r\n const renderCellButton = useCallback((\r\n rowId: string, colId: string, state: CellState,\r\n config: typeof cellStateConfig[CellState],\r\n rowLocked: boolean, colLocked: boolean,\r\n row: R, col: C\r\n ) => {\r\n const Icon = config.icon;\r\n const isEditable = isCellEditable(rowLocked, colLocked);\r\n\r\n return (\r\n <td\r\n key={`${rowId}-${colId}`}\r\n className=\"px-0 py-1 text-center\"\r\n style={{ width: cellWidth }}\r\n >\r\n <button\r\n type=\"button\"\r\n onClick={() => handleCellClick(rowId, colId, rowLocked, colLocked)}\r\n disabled={!isEditable}\r\n className={getCellButtonClass(isEditable, state)}\r\n style={{ backgroundColor: config.bg }}\r\n title={`${getRowLabel(row)} × ${getColumnLabel(col)}: ${config.label}`}\r\n >\r\n {state !== 'none' && (\r\n <Icon className=\"w-4 h-4\" style={{ color: config.color }} />\r\n )}\r\n </button>\r\n </td>\r\n );\r\n }, [onCellClick, canEditLocked, handleCellClick, cellWidth, getRowLabel, getColumnLabel, isCellEditable, getCellButtonClass]);\r\n\r\n const renderSubGroupRow = useCallback((\r\n row: R,\r\n rowIndex: number,\r\n columns: C[],\r\n rowColumnWidth: string,\r\n getCellState: (rowId: string, colId: string) => CellState\r\n ) => {\r\n const rowId = getRowId(row);\r\n const rowLocked = isRowLocked?.(row) || false;\r\n const rowIcon = getRowIcon?.(row);\r\n const bgClass = rowIndex % 2 === 0 ? 'bg-[var(--bg-primary)]' : 'bg-[var(--bg-secondary)]/50';\r\n const rowBgClass = rowIndex % 2 === 0 ? '' : 'bg-[var(--bg-secondary)]/50';\r\n\r\n return (\r\n <tr\r\n key={rowId}\r\n className={`hover:bg-[var(--bg-tertiary)] ${rowBgClass}`}\r\n >\r\n <td\r\n className={`sticky left-0 z-10 px-3 py-2 pl-12 ${bgClass}`}\r\n style={{ width: rowColumnWidth }}\r\n >\r\n <div className=\"flex items-center gap-2\">\r\n {rowIcon}\r\n {rowLocked && <Lock className=\"w-3 h-3 text-[var(--warning-text)]\" />}\r\n <span className=\"text-sm text-[var(--text-primary)]\">\r\n {getRowLabel(row)}\r\n </span>\r\n </div>\r\n </td>\r\n {columns.map((col) => {\r\n const colId = getColumnId(col);\r\n const colLocked = isColumnLocked?.(col) || false;\r\n const state = getCellState(rowId, colId);\r\n const config = cellStateConfig[state];\r\n return renderCellButton(rowId, colId, state, config, rowLocked, colLocked, row, col);\r\n })}\r\n </tr>\r\n );\r\n }, [getRowId, isRowLocked, getRowIcon, getRowLabel, getColumnId, isColumnLocked, renderCellButton]);\r\n\r\n if (loading) {\r\n return (\r\n <div className=\"flex items-center justify-center py-12\">\r\n <Loader2 className=\"w-8 h-8 animate-spin text-[var(--color-primary-500)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n if (rows.length === 0 || columns.length === 0) {\r\n return (\r\n <div className=\"text-center py-12 text-[var(--text-secondary)]\">\r\n {emptyMessage}\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className={`space-y-4 ${className}`}>\r\n {/* Search and Legend */}\r\n <div className=\"flex items-center justify-between gap-4\">\r\n {searchable && (\r\n <div className=\"relative max-w-xs\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-secondary)]\" />\r\n <input\r\n type=\"text\"\r\n value={searchTerm}\r\n onChange={(e) => setSearchTerm(e.target.value)}\r\n placeholder=\"Rechercher...\"\r\n className=\"input pl-10 w-full\"\r\n />\r\n </div>\r\n )}\r\n\r\n {showLegend && (\r\n <div className=\"flex items-center gap-4 text-xs\">\r\n {Object.entries(cellStateConfig).map(([state, config]) => {\r\n if (state === 'none') return null;\r\n const Icon = config.icon;\r\n return (\r\n <div key={state} className=\"flex items-center gap-1\">\r\n <div\r\n className=\"w-5 h-5 rounded flex items-center justify-center\"\r\n style={{ backgroundColor: config.bg }}\r\n >\r\n <Icon className=\"w-3 h-3\" style={{ color: config.color }} />\r\n </div>\r\n <span className=\"text-[var(--text-secondary)]\">{config.label}</span>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Matrix Table */}\r\n <div className=\"overflow-x-auto border border-[var(--border-color)] rounded-lg\">\r\n <table className=\"border-collapse\" style={{ width: 'auto' }}>\r\n <thead className=\"bg-[var(--bg-secondary)]\">\r\n <tr>\r\n {/* Row header column */}\r\n <th\r\n className=\"sticky left-0 z-20 bg-[var(--bg-secondary)] px-3 text-left text-xs font-medium text-[var(--text-secondary)] uppercase tracking-wider border-b border-[var(--border-color)]\"\r\n style={{ width: rowColumnWidth, minWidth: rowColumnWidth, height: '120px', verticalAlign: 'bottom', paddingBottom: '12px' }}\r\n >\r\n Ressource\r\n </th>\r\n\r\n {/* Column headers (rotated) */}\r\n {columns.map((col) => {\r\n const colId = getColumnId(col);\r\n const label = getColumnShortLabel?.(col) || getColumnLabel(col);\r\n const locked = isColumnLocked?.(col);\r\n const color = getColumnColor?.(col);\r\n\r\n return (\r\n <th\r\n key={colId}\r\n className=\"relative overflow-visible border-b border-[var(--border-color)]\"\r\n style={{ width: cellWidth, minWidth: cellWidth, maxWidth: cellWidth, height: '120px', padding: 0, verticalAlign: 'bottom' }}\r\n >\r\n <div\r\n className=\"absolute bottom-2 left-1/2\"\r\n style={{\r\n transform: 'translateX(-6px) rotate(-55deg)',\r\n transformOrigin: 'bottom left',\r\n width: '100px',\r\n }}\r\n >\r\n <div className=\"flex items-center gap-1\">\r\n {locked && <Lock className=\"w-3 h-3 flex-shrink-0 text-[var(--warning-text)]\" />}\r\n <span\r\n className=\"text-[10px] font-medium text-[var(--text-primary)] whitespace-nowrap\"\r\n style={{ color: color }}\r\n title={getColumnLabel(col)}\r\n >\r\n {label}\r\n </span>\r\n </div>\r\n </div>\r\n </th>\r\n );\r\n })}\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n {Array.from(filteredGroups.entries()).map(([group, groupData]) => {\r\n const isCollapsed = collapsedGroups.has(group);\r\n const isDefaultGroup = group === '_default';\r\n const totalRows = groupData.rows.length + Array.from(groupData.subGroups.values()).reduce((sum, arr) => sum + arr.length, 0);\r\n\r\n return (\r\n <tbody key={group}>\r\n {/* Group Header (if not default) */}\r\n {!isDefaultGroup && (\r\n <tr className=\"bg-[var(--color-accent-900)]/30\">\r\n <td\r\n className=\"sticky left-0 z-10 px-3 py-2 bg-[var(--color-accent-900)]/30\"\r\n style={{ width: rowColumnWidth }}\r\n >\r\n <button\r\n onClick={() => toggleGroup(group)}\r\n className=\"flex items-center gap-2 w-full text-left hover:opacity-80\"\r\n >\r\n {isCollapsed ? (\r\n <ChevronRight className=\"w-4 h-4 text-[var(--color-accent-400)]\" />\r\n ) : (\r\n <ChevronDown className=\"w-4 h-4 text-[var(--color-accent-400)]\" />\r\n )}\r\n <span className=\"text-sm font-bold text-[var(--color-accent-300)] uppercase\">\r\n {group}\r\n </span>\r\n <span className=\"text-xs text-[var(--color-accent-400)]\">\r\n ({totalRows})\r\n </span>\r\n </button>\r\n </td>\r\n <td colSpan={columns.length} />\r\n </tr>\r\n )}\r\n\r\n {/* Rows (if not collapsed) */}\r\n {!isCollapsed && (\r\n <>\r\n {/* Direct rows */}\r\n {groupData.rows.map((row, rowIndex) => {\r\n const rowId = getRowId(row);\r\n const rowLocked = isRowLocked?.(row) || false;\r\n const rowIcon = getRowIcon?.(row);\r\n const childCount = getRowChildCount?.(row);\r\n const bgClass = rowIndex % 2 === 0 ? 'bg-[var(--bg-primary)]' : 'bg-[var(--bg-secondary)]/50';\r\n const rowBgClass = rowIndex % 2 === 0 ? '' : 'bg-[var(--bg-secondary)]/50';\r\n\r\n return (\r\n <tr\r\n key={rowId}\r\n className={`hover:bg-[var(--bg-tertiary)] ${rowBgClass}`}\r\n >\r\n <td\r\n className={`sticky left-0 z-10 px-3 py-2 ${bgClass}`}\r\n style={{ width: rowColumnWidth }}\r\n >\r\n <div className=\"flex items-center gap-2\">\r\n {rowIcon}\r\n {rowLocked && <Lock className=\"w-3 h-3 text-[var(--warning-text)]\" />}\r\n <span className=\"text-sm font-medium text-[var(--text-primary)]\">\r\n {getRowLabel(row)}\r\n </span>\r\n {childCount !== undefined && (\r\n <span className=\"text-xs text-[var(--text-secondary)]\">\r\n ({childCount})\r\n </span>\r\n )}\r\n </div>\r\n </td>\r\n {columns.map((col) => {\r\n const colId = getColumnId(col);\r\n const colLocked = isColumnLocked?.(col) || false;\r\n const state = getCellState(rowId, colId);\r\n const config = cellStateConfig[state];\r\n return renderCellButton(rowId, colId, state, config, rowLocked, colLocked, row, col);\r\n })}\r\n </tr>\r\n );\r\n })}\r\n\r\n {/* Sub-group rows */}\r\n {Array.from(groupData.subGroups.entries()).map(([subGroup, subRows]) => {\r\n return (\r\n <tbody key={subGroup}>\r\n <tr className=\"bg-[var(--bg-tertiary)]\">\r\n <td\r\n className=\"sticky left-0 z-10 px-3 py-1 pl-8 bg-[var(--bg-tertiary)]\"\r\n style={{ width: rowColumnWidth }}\r\n >\r\n <span className=\"text-xs font-semibold text-[var(--text-secondary)] uppercase\">\r\n {subGroup} ({subRows.length})\r\n </span>\r\n </td>\r\n <td colSpan={columns.length} />\r\n </tr>\r\n {subRows.map((row, rowIndex) => renderSubGroupRow(row, rowIndex, columns, rowColumnWidth, getCellState))}\r\n </tbody>\r\n );\r\n })}\r\n </>\r\n )}\r\n </tbody>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nexport default PermissionMatrix;\r\n","import type { ReactElement } from 'react';\nimport { Sun, Moon, RotateCcw, Check, Palette, Square, Circle, Layers, RefreshCw, Loader2, Globe, Building2 } from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useTheme } from '@/contexts/ThemeContext';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { ACCENT_COLORS, THEME_PRESETS, ITEM_PALETTES } from '@/config/themePresets';\r\nimport { BORDER_RADIUS_VALUES, BORDER_RADIUS_LABELS, type BorderRadiusSize, type BorderRadiusConfig, type ItemPaletteKey } from '@/types/theme';\r\n\r\ninterface ThemeCustomizerProps {\r\n readonly onSync?: () => void;\r\n readonly isSyncing?: boolean;\r\n readonly syncSuccess?: boolean;\r\n}\r\n\r\nconst RADIUS_OPTIONS: { size: BorderRadiusSize; icon: 'square' | 'rounded' }[] = [\r\n { size: 'none', icon: 'square' },\r\n { size: 'small', icon: 'rounded' },\r\n { size: 'medium', icon: 'rounded' },\r\n { size: 'large', icon: 'rounded' },\r\n { size: 'xlarge', icon: 'rounded' },\r\n { size: 'xxlarge', icon: 'rounded' },\r\n];\r\n\r\ninterface RadiusSelectorProps {\r\n readonly label: string;\r\n readonly element: keyof BorderRadiusConfig;\r\n readonly value: BorderRadiusSize;\r\n readonly onChange: (element: keyof BorderRadiusConfig, size: BorderRadiusSize) => void;\r\n}\r\n\r\nfunction RadiusSelector({ label, element, value, onChange }: RadiusSelectorProps) {\r\n return (\r\n <div className=\"space-y-2\">\r\n <label className=\"text-sm font-medium text-[var(--text-secondary)]\">{label}</label>\r\n <div className=\"flex flex-wrap gap-2\">\r\n {RADIUS_OPTIONS.map(({ size }) => (\r\n <button\r\n key={size}\r\n onClick={() => onChange(element, size)}\r\n className={`px-3 py-2 text-xs font-medium transition-all ${\r\n value === size\r\n ? 'bg-[var(--color-accent-500)] text-white'\r\n : 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]'\r\n }`}\r\n style={{ borderRadius: BORDER_RADIUS_VALUES[size] }}\r\n >\r\n {BORDER_RADIUS_LABELS[size]}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nconst ScopeSelector = ({ selectedThemeTenant, setSelectedThemeTenant, currentTenant, t }: any) => {\r\n return (\r\n <section className=\"space-y-3\">\r\n <div className=\"flex items-center gap-2\">\r\n <Globe className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)] uppercase tracking-wider\">\r\n {t('theme.scope.title', 'Portée du thème')}\r\n </h3>\r\n </div>\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {t('theme.scope.description', 'Choisissez si ce thème s\\'applique à tous vos espaces ou uniquement à l\\'espace actuel')}\r\n </p>\r\n <div className=\"flex gap-3\">\r\n <button\r\n type=\"button\"\r\n onClick={() => setSelectedThemeTenant(null)}\r\n className={`flex-1 flex items-center gap-3 p-4 transition-all border ${\r\n selectedThemeTenant === null\r\n ? 'border-[var(--color-accent-500)] bg-[var(--accent-bg)]'\r\n : 'border-[var(--border-color)] bg-[var(--bg-secondary)] hover:border-[var(--border-strong)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-card)' }}\r\n >\r\n <Globe className={`w-5 h-5 ${selectedThemeTenant === null ? 'text-[var(--color-accent-500)]' : 'text-[var(--text-muted)]'}`} />\r\n <div className=\"text-left\">\r\n <span className={`font-medium ${selectedThemeTenant === null ? 'text-[var(--text-primary)]' : 'text-[var(--text-secondary)]'}`}>\r\n {t('theme.scope.global', 'Global')}\r\n </span>\r\n <p className=\"text-xs text-[var(--text-muted)] mt-0.5\">\r\n {t('theme.scope.globalDesc', 'Tous les espaces')}\r\n </p>\r\n </div>\r\n {selectedThemeTenant === null && (\r\n <Check className=\"w-4 h-4 text-[var(--color-accent-500)] ml-auto\" />\r\n )}\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => setSelectedThemeTenant(currentTenant.id)}\r\n className={`flex-1 flex items-center gap-3 p-4 transition-all border ${\r\n selectedThemeTenant === currentTenant.id\r\n ? 'border-[var(--color-accent-500)] bg-[var(--accent-bg)]'\r\n : 'border-[var(--border-color)] bg-[var(--bg-secondary)] hover:border-[var(--border-strong)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-card)' }}\r\n >\r\n <Building2 className={`w-5 h-5 ${selectedThemeTenant === currentTenant.id ? 'text-[var(--color-accent-500)]' : 'text-[var(--text-muted)]'}`} />\r\n <div className=\"text-left\">\r\n <span className={`font-medium ${selectedThemeTenant === currentTenant.id ? 'text-[var(--text-primary)]' : 'text-[var(--text-secondary)]'}`}>\r\n {currentTenant.name}\r\n </span>\r\n <p className=\"text-xs text-[var(--text-muted)] mt-0.5\">\r\n {t('theme.scope.tenantDesc', 'Espace actuel uniquement')}\r\n </p>\r\n </div>\r\n {selectedThemeTenant === currentTenant.id && (\r\n <Check className=\"w-4 h-4 text-[var(--color-accent-500)] ml-auto\" />\r\n )}\r\n </button>\r\n </div>\r\n </section>\r\n );\r\n};\r\n\r\nexport function ThemeCustomizer({ onSync, isSyncing = false, syncSuccess = false }: ThemeCustomizerProps): ReactElement {\r\n const { t } = useTranslation('preferences');\r\n const {\r\n config,\r\n mode,\r\n setMode,\r\n setBorderRadius,\r\n setAccentColor,\r\n setItemPalette,\r\n applyPreset,\r\n resetToDefault,\r\n selectedThemeTenant,\r\n setSelectedThemeTenant,\r\n } = useTheme();\r\n const { currentTenant, hasMultipleTenants } = useTenant();\r\n\r\n // Determine if we should show scope selector\r\n const showScopeSelector = hasMultipleTenants && currentTenant;\r\n\r\n\r\n return (\r\n <div className=\"space-y-8\">\r\n <div className=\"flex items-center justify-between\">\r\n <div>\r\n <h2 className=\"text-xl font-bold text-[var(--text-primary)]\">Personnalisation</h2>\r\n <p className=\"text-sm text-[var(--text-secondary)] mt-1\">\r\n Personnalisez l'apparence de l'interface\r\n </p>\r\n </div>\r\n <div className=\"flex items-center gap-2\">\r\n {onSync && (\r\n <button\r\n type=\"button\"\r\n onClick={onSync}\r\n disabled={isSyncing}\r\n className=\"flex items-center gap-2 px-3 py-2 text-sm font-medium text-white bg-[var(--color-accent-600)] hover:bg-[var(--color-accent-700)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n >\r\n {isSyncing && (\r\n <>\r\n <Loader2 className=\"w-4 h-4 animate-spin\" />\r\n Sync...\r\n </>\r\n )}\r\n {!isSyncing && syncSuccess && (\r\n <>\r\n <Check className=\"w-4 h-4\" />\r\n Synchronisé\r\n </>\r\n )}\r\n {!isSyncing && !syncSuccess && (\r\n <>\r\n <RefreshCw className=\"w-4 h-4\" />\r\n Synchroniser\r\n </>\r\n )}\r\n </button>\r\n )}\r\n <button\r\n onClick={resetToDefault}\r\n className=\"flex items-center gap-2 px-3 py-2 text-sm text-[var(--text-secondary)] bg-[var(--bg-tertiary)] hover:bg-[var(--bg-hover)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n >\r\n <RotateCcw className=\"w-4 h-4\" />\r\n Réinitialiser\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {/* Theme Scope Selector - only for multi-tenant users */}\r\n {showScopeSelector && (\r\n <ScopeSelector\r\n selectedThemeTenant={selectedThemeTenant}\r\n setSelectedThemeTenant={setSelectedThemeTenant}\r\n currentTenant={currentTenant}\r\n t={t}\r\n />\r\n )}\r\n\r\n <section className=\"space-y-4\">\r\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)] uppercase tracking-wider\">\r\n Thèmes prédéfinis\r\n </h3>\r\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-3\">\r\n {THEME_PRESETS.map((preset) => {\r\n const isActive =\r\n config.accentColorKey === preset.config.accentColorKey &&\r\n JSON.stringify(config.borderRadius) === JSON.stringify(preset.config.borderRadius);\r\n\r\n return (\r\n <button\r\n key={preset.id}\r\n onClick={() => applyPreset(preset.config)}\r\n className={`p-4 text-left transition-all border ${\r\n isActive\r\n ? 'border-[var(--color-accent-500)] bg-[var(--accent-bg)]'\r\n : 'border-[var(--border-color)] bg-[var(--bg-secondary)] hover:border-[var(--border-strong)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-card)' }}\r\n >\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <span className=\"text-2xl\">{preset.icon}</span>\r\n {isActive && (\r\n <Check className=\"w-4 h-4 text-[var(--color-accent-500)]\" />\r\n )}\r\n </div>\r\n <div className=\"font-semibold text-[var(--text-primary)]\">{preset.name}</div>\r\n <div className=\"text-xs text-[var(--text-muted)] mt-1\">{preset.description}</div>\r\n </button>\r\n );\r\n })}\r\n </div>\r\n </section>\r\n\r\n <section className=\"space-y-4\">\r\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)] uppercase tracking-wider\">\r\n Mode d'affichage\r\n </h3>\r\n <div className=\"flex gap-3\">\r\n <button\r\n onClick={() => setMode('light')}\r\n className={`flex-1 flex items-center justify-center gap-3 p-4 transition-all border ${\r\n mode === 'light'\r\n ? 'border-[var(--color-accent-500)] bg-[var(--accent-bg)]'\r\n : 'border-[var(--border-color)] bg-[var(--bg-secondary)] hover:border-[var(--border-strong)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-card)' }}\r\n >\r\n <Sun className={`w-5 h-5 ${mode === 'light' ? 'text-amber-500' : 'text-[var(--text-muted)]'}`} />\r\n <span className={`font-medium ${mode === 'light' ? 'text-[var(--text-primary)]' : 'text-[var(--text-secondary)]'}`}>\r\n Clair\r\n </span>\r\n </button>\r\n <button\r\n onClick={() => setMode('dark')}\r\n className={`flex-1 flex items-center justify-center gap-3 p-4 transition-all border ${\r\n mode === 'dark'\r\n ? 'border-[var(--color-accent-500)] bg-[var(--accent-bg)]'\r\n : 'border-[var(--border-color)] bg-[var(--bg-secondary)] hover:border-[var(--border-strong)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-card)' }}\r\n >\r\n <Moon className={`w-5 h-5 ${mode === 'dark' ? 'text-[var(--color-accent-400)]' : 'text-[var(--text-muted)]'}`} />\r\n <span className={`font-medium ${mode === 'dark' ? 'text-[var(--text-primary)]' : 'text-[var(--text-secondary)]'}`}>\r\n Sombre\r\n </span>\r\n </button>\r\n </div>\r\n </section>\r\n\r\n <section className=\"space-y-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <Palette className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)] uppercase tracking-wider\">\r\n Couleur d'accent\r\n </h3>\r\n </div>\r\n <div className=\"flex flex-wrap gap-3\">\r\n {Object.entries(ACCENT_COLORS).map(([key, { name, shades }]) => (\r\n <button\r\n key={key}\r\n onClick={() => setAccentColor(key)}\r\n className={`relative w-10 h-10 transition-all ${\r\n config.accentColorKey === key ? 'ring-2 ring-offset-2 ring-[var(--border-strong)]' : ''\r\n }`}\r\n style={{\r\n backgroundColor: shades[500],\r\n borderRadius: 'var(--radius-button)',\r\n }}\r\n title={name}\r\n >\r\n {config.accentColorKey === key && (\r\n <Check className=\"absolute inset-0 m-auto w-5 h-5 text-white\" />\r\n )}\r\n </button>\r\n ))}\r\n </div>\r\n <p className=\"text-xs text-[var(--text-muted)]\">\r\n Couleur actuelle: <span className=\"font-medium text-[var(--color-accent-500)]\">{ACCENT_COLORS[config.accentColorKey]?.name}</span>\r\n </p>\r\n </section>\r\n\r\n <section className=\"space-y-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <Layers className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)] uppercase tracking-wider\">\r\n Palette des éléments\r\n </h3>\r\n </div>\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n Choisissez une palette de couleurs pour les cards et éléments de l'interface\r\n </p>\r\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-3\">\r\n {(Object.entries(ITEM_PALETTES) as [ItemPaletteKey, typeof ITEM_PALETTES[ItemPaletteKey]][]).map(([key, palette]) => {\r\n const colors = mode === 'dark' ? palette.colors.dark : palette.colors.light;\r\n const isActive = config.itemPaletteKey === key;\r\n\r\n return (\r\n <button\r\n key={key}\r\n onClick={() => setItemPalette(key)}\r\n className={`p-4 text-left transition-all border ${\r\n isActive\r\n ? 'border-[var(--color-accent-500)] ring-2 ring-[var(--color-accent-500)] ring-opacity-30'\r\n : 'border-[var(--border-color)] hover:border-[var(--border-strong)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-card)' }}\r\n >\r\n <div className=\"flex items-center justify-between mb-3\">\r\n <span className=\"text-xl\">{palette.icon}</span>\r\n {isActive && (\r\n <Check className=\"w-4 h-4 text-[var(--color-accent-500)]\" />\r\n )}\r\n </div>\r\n <div className=\"flex h-8 mb-3 rounded overflow-hidden border\" style={{ borderColor: colors.dark }}>\r\n <div className=\"flex-1\" style={{ backgroundColor: colors.light }} />\r\n <div className=\"flex-1\" style={{ backgroundColor: colors.medium }} />\r\n <div className=\"flex-1\" style={{ backgroundColor: colors.dark }} />\r\n </div>\r\n <div className=\"font-semibold text-[var(--text-primary)] text-sm\">{palette.name}</div>\r\n </button>\r\n );\r\n })}\r\n </div>\r\n <p className=\"text-xs text-[var(--text-muted)]\">\r\n Palette actuelle: <span className=\"font-medium text-[var(--color-accent-500)]\">{ITEM_PALETTES[config.itemPaletteKey]?.name}</span>\r\n </p>\r\n </section>\r\n\r\n <section className=\"space-y-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <Square className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)] uppercase tracking-wider\">\r\n Style des coins\r\n </h3>\r\n </div>\r\n <div className=\"space-y-4 p-4 bg-[var(--bg-secondary)] border border-[var(--border-color)]\" style={{ borderRadius: 'var(--radius-card)' }}>\r\n <RadiusSelector\r\n label=\"Cards & Containers\"\r\n element=\"card\"\r\n value={config.borderRadius.card}\r\n onChange={setBorderRadius}\r\n />\r\n <RadiusSelector\r\n label=\"Boutons\"\r\n element=\"button\"\r\n value={config.borderRadius.button}\r\n onChange={setBorderRadius}\r\n />\r\n <RadiusSelector\r\n label=\"Badges\"\r\n element=\"badge\"\r\n value={config.borderRadius.badge}\r\n onChange={setBorderRadius}\r\n />\r\n <RadiusSelector\r\n label=\"Champs de saisie\"\r\n element=\"input\"\r\n value={config.borderRadius.input}\r\n onChange={setBorderRadius}\r\n />\r\n <RadiusSelector\r\n label=\"Modales\"\r\n element=\"modal\"\r\n value={config.borderRadius.modal}\r\n onChange={setBorderRadius}\r\n />\r\n <RadiusSelector\r\n label=\"Éléments de menu\"\r\n element=\"menuItem\"\r\n value={config.borderRadius.menuItem}\r\n onChange={setBorderRadius}\r\n />\r\n </div>\r\n </section>\r\n\r\n <section className=\"space-y-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <Circle className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n <h3 className=\"text-sm font-semibold text-[var(--text-primary)] uppercase tracking-wider\">\r\n Aperçu\r\n </h3>\r\n </div>\r\n <div className=\"p-6 bg-[var(--bg-secondary)] border border-[var(--border-color)]\" style={{ borderRadius: 'var(--radius-card)' }}>\r\n <div className=\"space-y-4\">\r\n <div className=\"flex flex-wrap gap-2\">\r\n <span className=\"badge-success px-3 py-1 text-xs font-medium\">Succès</span>\r\n <span className=\"badge-warning px-3 py-1 text-xs font-medium\">Attention</span>\r\n <span className=\"badge-error px-3 py-1 text-xs font-medium\">Erreur</span>\r\n <span className=\"badge-info px-3 py-1 text-xs font-medium\">Info</span>\r\n <span className=\"badge-accent px-3 py-1 text-xs font-medium\">Accent</span>\r\n </div>\r\n\r\n <div className=\"flex flex-wrap gap-3\">\r\n <button className=\"btn btn-primary\">Bouton primaire</button>\r\n <button className=\"btn btn-secondary\">Bouton secondaire</button>\r\n <button className=\"btn btn-ghost\">Bouton ghost</button>\r\n </div>\r\n\r\n <div>\r\n <input\r\n type=\"text\"\r\n placeholder=\"Champ de saisie exemple...\"\r\n className=\"input w-full\"\r\n />\r\n </div>\r\n\r\n <div className=\"card p-4\">\r\n <div className=\"flex items-center gap-3\">\r\n <div\r\n className=\"w-10 h-10 bg-gradient-to-br from-[var(--color-accent-500)] to-[var(--color-accent-600)] flex items-center justify-center text-white font-bold\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n >\r\n A\r\n </div>\r\n <div>\r\n <div className=\"font-semibold text-[var(--text-primary)]\">Exemple de card ({ITEM_PALETTES[config.itemPaletteKey]?.name})</div>\r\n <div className=\"text-sm text-[var(--text-secondary)]\">Cette card utilise la palette sélectionnée</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"grid grid-cols-2 gap-3\">\r\n <div className=\"card p-3\">\r\n <div className=\"text-sm font-medium text-[var(--text-primary)]\">Card 1</div>\r\n <div className=\"text-xs text-[var(--text-muted)]\">Aperçu du style</div>\r\n </div>\r\n <div className=\"card p-3\">\r\n <div className=\"text-sm font-medium text-[var(--text-primary)]\">Card 2</div>\r\n <div className=\"text-xs text-[var(--text-muted)]\">Aperçu du style</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </section>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { Construction, ArrowLeft } from 'lucide-react';\r\nimport { useNavigate } from 'react-router-dom';\r\nimport { useTranslation } from 'react-i18next';\r\n\r\ninterface UnderDevelopmentProps {\r\n /** Title to display, defaults to i18n key */\r\n readonly title?: string;\r\n /** Description to display, defaults to i18n key */\r\n readonly description?: string;\r\n /** Icon to display, defaults to Construction */\r\n readonly icon?: React.ReactNode;\r\n /** Show back button */\r\n readonly showBackButton?: boolean;\r\n /** Custom back URL */\r\n readonly backUrl?: string;\r\n /** Additional content to display below the message */\r\n readonly children?: React.ReactNode;\r\n}\r\n\r\nexport function UnderDevelopment({\r\n title,\r\n description,\r\n icon,\r\n showBackButton = true,\r\n backUrl,\r\n children\r\n}: UnderDevelopmentProps): ReactElement {\r\n const { t } = useTranslation('common');\r\n const navigate = useNavigate();\r\n\r\n const handleBack = () => {\r\n if (backUrl) {\r\n navigate(backUrl);\r\n } else {\r\n navigate(-1);\r\n }\r\n };\r\n\r\n return (\r\n <div className=\"flex flex-col items-center justify-center min-h-[400px] p-8\">\r\n <div className=\"max-w-md w-full text-center\">\r\n {/* Icon */}\r\n <div className=\"w-20 h-20 mx-auto mb-6 rounded-full bg-gradient-to-br from-amber-100 to-orange-100 dark:from-amber-900/30 dark:to-orange-900/30 flex items-center justify-center\">\r\n {icon || <Construction className=\"w-10 h-10 text-amber-600 dark:text-amber-400\" />}\r\n </div>\r\n\r\n {/* Title */}\r\n <h2 className=\"text-2xl font-bold text-[var(--text-primary)] mb-3\">\r\n {title || t('underDevelopment.title', 'En cours de développement')}\r\n </h2>\r\n\r\n {/* Description */}\r\n <p className=\"text-[var(--text-secondary)] mb-6\">\r\n {description || t('underDevelopment.description', 'Cette fonctionnalité est actuellement en cours de développement et sera disponible prochainement.')}\r\n </p>\r\n\r\n {/* Progress indicator */}\r\n <div className=\"flex items-center justify-center gap-2 mb-8\">\r\n <div className=\"flex gap-1\">\r\n <span className=\"w-2 h-2 rounded-full bg-amber-500 animate-pulse\" style={{ animationDelay: '0ms' }} />\r\n <span className=\"w-2 h-2 rounded-full bg-amber-500 animate-pulse\" style={{ animationDelay: '150ms' }} />\r\n <span className=\"w-2 h-2 rounded-full bg-amber-500 animate-pulse\" style={{ animationDelay: '300ms' }} />\r\n </div>\r\n <span className=\"text-sm text-[var(--text-muted)]\">\r\n {t('underDevelopment.workInProgress', 'Travail en cours')}\r\n </span>\r\n </div>\r\n\r\n {/* Additional content */}\r\n {children}\r\n\r\n {/* Back button */}\r\n {showBackButton && (\r\n <button\r\n onClick={handleBack}\r\n className=\"btn btn-secondary inline-flex items-center gap-2\"\r\n >\r\n <ArrowLeft className=\"w-4 h-4\" />\r\n {t('common.back', 'Retour')}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useLocation, Link } from 'react-router-dom';\r\nimport { Users, Key, LayoutDashboard, X, Star, Settings, ChevronDown, ChevronRight } from 'lucide-react';\r\nimport * as LucideIcons from 'lucide-react';\r\nimport type { LucideIcon } from 'lucide-react';\r\nimport { useFavoriteModules } from '@/hooks/useFavoriteModules';\r\nimport { useFavorites } from '@/contexts/FavoritesContext';\r\n\r\ninterface AdminSidebarProps {\r\n readonly isOpen?: boolean;\r\n readonly onClose?: () => void;\r\n}\r\n\r\n// Dynamic icon component\r\nfunction DynamicIcon({ name, className }: { name: string; className?: string }) {\r\n const icons = LucideIcons as unknown as Record<string, LucideIcon>;\r\n const IconComponent = icons[name];\r\n if (!IconComponent) {\r\n return <Star className={className} />;\r\n }\r\n return <IconComponent className={className} />;\r\n}\r\n\r\nconst menuItems = [\r\n { path: '/administration', icon: LayoutDashboard, labelKey: 'sidebar.dashboard', exact: true },\r\n { path: '/administration/users', icon: Users, labelKey: 'sidebar.users' },\r\n { path: '/administration/roles', icon: Key, labelKey: 'sidebar.roles' },\r\n];\r\n\r\nexport function AdminSidebar({ isOpen = true, onClose }: AdminSidebarProps): ReactElement {\r\n const { t } = useTranslation(['navigation', 'common']);\r\n const location = useLocation();\r\n const { favorites, loading: favoritesLoading } = useFavoriteModules();\r\n const { expandFavorites, clearExpand } = useFavorites();\r\n const [favoritesCollapsed, setFavoritesCollapsed] = useState(false);\r\n\r\n // Auto-expand favorites section when triggered from quick access\r\n useEffect(() => {\r\n if (expandFavorites) {\r\n // Use queueMicrotask to defer setState and avoid cascading renders\r\n queueMicrotask(() => {\r\n setFavoritesCollapsed(false);\r\n });\r\n clearExpand();\r\n }\r\n }, [expandFavorites, clearExpand]);\r\n\r\n const isActive = (path: string, exact?: boolean) => {\r\n if (exact) {\r\n return location.pathname === path;\r\n }\r\n return location.pathname.startsWith(path);\r\n };\r\n\r\n const sidebarContent = (\r\n <div className=\"flex flex-col h-full\">\r\n <div className=\"lg:hidden flex items-center justify-between p-4 border-b border-[var(--border-color)]\">\r\n <span className=\"font-semibold\">Menu</span>\r\n <button onClick={onClose} className=\"p-2 hover:bg-[var(--bg-tertiary)]\" style={{ borderRadius: 'var(--radius-button)' }}>\r\n <X className=\"w-5 h-5\" />\r\n </button>\r\n </div>\r\n\r\n <nav className=\"flex-1 p-4 space-y-1 overflow-y-auto\">\r\n {/* Favorites Section */}\r\n {!favoritesLoading && favorites.length > 0 && (\r\n <div className=\"mb-4\">\r\n <div className=\"flex items-center justify-between px-3 py-2\">\r\n <button\r\n onClick={() => setFavoritesCollapsed(!favoritesCollapsed)}\r\n className=\"flex items-center gap-2 text-xs font-semibold text-[var(--text-muted)] uppercase tracking-wider hover:text-[var(--text-primary)] transition-colors\"\r\n >\r\n {favoritesCollapsed ? (\r\n <ChevronRight className=\"w-3.5 h-3.5\" />\r\n ) : (\r\n <ChevronDown className=\"w-3.5 h-3.5\" />\r\n )}\r\n <Star className=\"w-3.5 h-3.5\" />\r\n {t('common:favorites.quickAccess')}\r\n </button>\r\n <Link\r\n to=\"/myspace/preferences?tab=favorites\"\r\n onClick={onClose}\r\n className=\"p-1 text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n title={t('common:favorites.manage')}\r\n >\r\n <Settings className=\"w-3.5 h-3.5\" />\r\n </Link>\r\n </div>\r\n {!favoritesCollapsed && (\r\n <div className=\"space-y-0.5\">\r\n {favorites.map((module) => {\r\n const active = location.pathname === module.route;\r\n return (\r\n <Link\r\n key={module.id}\r\n to={module.route}\r\n onClick={onClose}\r\n className={`flex items-center gap-3 px-3 py-2 transition-colors ${\r\n active\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n title={`${module.label} (${module.applicationLabel})`}\r\n >\r\n <DynamicIcon name={module.icon} className=\"w-4 h-4\" />\r\n <span className=\"text-sm font-medium truncate\">{module.label}</span>\r\n </Link>\r\n );\r\n })}\r\n </div>\r\n )}\r\n <div className=\"my-3 border-b border-[var(--border-color)]\" />\r\n </div>\r\n )}\r\n\r\n {/* Main Menu */}\r\n {menuItems.map((item) => {\r\n const Icon = item.icon;\r\n const active = isActive(item.path, item.exact);\r\n\r\n return (\r\n <Link\r\n key={item.path}\r\n to={item.path}\r\n onClick={onClose}\r\n className={`flex items-center gap-3 px-3 py-2.5 transition-colors ${\r\n active\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n >\r\n <Icon className=\"w-5 h-5\" />\r\n <span className=\"font-medium\">{t(item.labelKey)}</span>\r\n </Link>\r\n );\r\n })}\r\n </nav>\r\n </div>\r\n );\r\n\r\n return (\r\n <>\r\n <aside className=\"hidden lg:block w-64 h-[calc(100vh-4rem)] fixed top-16 left-0 bg-[var(--bg-secondary)] border-r border-[var(--border-color)]\">\r\n {sidebarContent}\r\n </aside>\r\n\r\n {isOpen && (\r\n <>\r\n <button\r\n type=\"button\"\r\n className=\"lg:hidden fixed inset-0 bg-black/50 z-40\"\r\n onClick={onClose}\r\n aria-label=\"Close sidebar\"\r\n />\r\n <aside className=\"lg:hidden fixed top-0 left-0 h-full w-64 bg-[var(--bg-secondary)] border-r border-[var(--border-color)] z-50\">\r\n {sidebarContent}\r\n </aside>\r\n </>\r\n )}\r\n </>\r\n );\r\n}\r\n","import { useState, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useLocation, Link } from 'react-router-dom';\r\nimport { LayoutDashboard, X, Star, Settings, ChevronDown, ChevronRight, PanelLeftClose, PanelLeft } from 'lucide-react';\r\nimport * as LucideIcons from 'lucide-react';\r\nimport type { LucideIcon } from 'lucide-react';\r\nimport { useFavoriteModules } from '@/hooks/useFavoriteModules';\r\nimport { useFavorites } from '@/contexts/FavoritesContext';\r\nimport { useSidebar } from '@/contexts/SidebarContext';\r\nimport { useNavigation } from '@/contexts/NavigationContext';\r\n\r\ninterface UserSidebarProps {\r\n readonly isOpen?: boolean;\r\n readonly onClose?: () => void;\r\n}\r\n\r\n// Dynamic icon component\r\nfunction DynamicIcon({ name, className }: { name: string; className?: string }) {\r\n const icons = LucideIcons as unknown as Record<string, LucideIcon>;\r\n const IconComponent = icons[name];\r\n if (!IconComponent) {\r\n return <Star className={className} />;\r\n }\r\n return <IconComponent className={className} />;\r\n}\r\n\r\nexport function UserSidebar({ isOpen = true, onClose }: UserSidebarProps): ReactElement {\r\n const { t } = useTranslation(['navigation', 'common']);\r\n const location = useLocation();\r\n const { favorites, loading: favoritesLoading } = useFavoriteModules();\r\n const { expandFavorites, clearExpand } = useFavorites();\r\n const { isCollapsed, toggleCollapsed } = useSidebar();\r\n const { getApplicationsByZone } = useNavigation();\r\n const [favoritesCollapsed, setFavoritesCollapsed] = useState(false);\r\n\r\n // Get the myspace application and its modules from the navigation menu\r\n const personalApps = getApplicationsByZone('personal');\r\n const myspaceApp = personalApps.find(app =>\r\n app.code === 'myspace' || app.code === 'core_myspace'\r\n );\r\n const dynamicModules = myspaceApp?.modules || [];\r\n\r\n // Auto-expand favorites section when triggered from quick access\r\n useEffect(() => {\r\n if (expandFavorites) {\r\n // Use queueMicrotask to defer setState and avoid cascading renders\r\n queueMicrotask(() => {\r\n setFavoritesCollapsed(false);\r\n });\r\n clearExpand();\r\n }\r\n }, [expandFavorites, clearExpand]);\r\n\r\n const isActive = (path: string, exact?: boolean) => {\r\n if (exact) {\r\n return location.pathname === path;\r\n }\r\n return location.pathname.startsWith(path);\r\n };\r\n\r\n const sidebarContent = (\r\n <div className=\"flex flex-col h-full\">\r\n <div className=\"lg:hidden flex items-center justify-between p-4 border-b border-[var(--border-color)]\">\r\n <span className=\"font-semibold\">Menu</span>\r\n <button onClick={onClose} className=\"p-2 hover:bg-[var(--bg-tertiary)]\" style={{ borderRadius: 'var(--radius-button)' }}>\r\n <X className=\"w-5 h-5\" />\r\n </button>\r\n </div>\r\n\r\n <nav className={`flex-1 overflow-y-auto space-y-1 ${isCollapsed ? 'p-2' : 'p-4'}`}>\r\n {/* Favorites Section */}\r\n {!favoritesLoading && favorites.length > 0 && (\r\n <div className=\"mb-4\">\r\n {!isCollapsed && (\r\n <div className=\"flex items-center justify-between px-3 py-2\">\r\n <button\r\n onClick={() => setFavoritesCollapsed(!favoritesCollapsed)}\r\n className=\"flex items-center gap-2 text-xs font-semibold text-[var(--text-muted)] uppercase tracking-wider hover:text-[var(--text-primary)] transition-colors\"\r\n >\r\n {favoritesCollapsed ? (\r\n <ChevronRight className=\"w-3.5 h-3.5\" />\r\n ) : (\r\n <ChevronDown className=\"w-3.5 h-3.5\" />\r\n )}\r\n <Star className=\"w-3.5 h-3.5\" />\r\n {t('common:favorites.quickAccess')}\r\n </button>\r\n <Link\r\n to=\"/myspace/preferences?tab=favorites\"\r\n onClick={onClose}\r\n className=\"p-1 text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n style={{ borderRadius: 'var(--radius-button)' }}\r\n title={t('common:favorites.manage')}\r\n >\r\n <Settings className=\"w-3.5 h-3.5\" />\r\n </Link>\r\n </div>\r\n )}\r\n {(isCollapsed || !favoritesCollapsed) && (\r\n <div className=\"space-y-0.5\">\r\n {favorites.map((module) => {\r\n const active = location.pathname === module.route;\r\n return (\r\n <Link\r\n key={module.id}\r\n to={module.route}\r\n onClick={onClose}\r\n className={`relative group flex items-center transition-colors ${\r\n isCollapsed ? 'justify-center px-2 py-2' : 'gap-3 px-3 py-2'\r\n } ${\r\n active\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n title={isCollapsed ? `${module.label} (${module.applicationLabel})` : undefined}\r\n >\r\n <DynamicIcon name={module.icon} className=\"w-4 h-4 flex-shrink-0\" />\r\n {!isCollapsed && (\r\n <span className=\"text-sm font-medium truncate\">{module.label}</span>\r\n )}\r\n {isCollapsed && (\r\n <div className=\"absolute left-full top-0 ml-2 px-3 py-2 bg-[var(--bg-secondary)] border border-[var(--border-color)] shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 whitespace-nowrap z-50\" style={{ borderRadius: 'var(--radius-menu-item)' }}>\r\n <span className=\"text-sm font-medium text-[var(--text-primary)]\">{module.label}</span>\r\n </div>\r\n )}\r\n </Link>\r\n );\r\n })}\r\n </div>\r\n )}\r\n {!isCollapsed && <div className=\"my-3 border-b border-[var(--border-color)]\" />}\r\n </div>\r\n )}\r\n\r\n {/* Dashboard Link */}\r\n <Link\r\n to=\"/myspace\"\r\n onClick={onClose}\r\n className={`relative group flex items-center transition-colors ${\r\n isCollapsed ? 'justify-center px-2 py-2.5' : 'gap-3 px-3 py-2.5'\r\n } ${\r\n isActive('/myspace', true)\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n title={isCollapsed ? t('sidebar.dashboard') : undefined}\r\n >\r\n <LayoutDashboard className=\"w-5 h-5 flex-shrink-0\" />\r\n {!isCollapsed && (\r\n <span className=\"font-medium\">{t('sidebar.dashboard')}</span>\r\n )}\r\n {isCollapsed && (\r\n <div className=\"absolute left-full top-0 ml-2 px-3 py-2 bg-[var(--bg-secondary)] border border-[var(--border-color)] shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 whitespace-nowrap z-50\" style={{ borderRadius: 'var(--radius-menu-item)' }}>\r\n <span className=\"text-sm font-medium text-[var(--text-primary)]\">{t('sidebar.dashboard')}</span>\r\n </div>\r\n )}\r\n </Link>\r\n\r\n {/* Dynamic Modules from Navigation API */}\r\n {dynamicModules.map((module) => {\r\n const modulePath = module.route || `/myspace/${module.code}`;\r\n const active = isActive(modulePath);\r\n\r\n return (\r\n <Link\r\n key={module.id}\r\n to={modulePath}\r\n onClick={onClose}\r\n className={`relative group flex items-center transition-colors ${\r\n isCollapsed ? 'justify-center px-2 py-2.5' : 'gap-3 px-3 py-2.5'\r\n } ${\r\n active\r\n ? 'bg-[var(--accent-bg)] text-[var(--color-accent-500)]'\r\n : 'text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n title={isCollapsed ? module.label : undefined}\r\n >\r\n <DynamicIcon name={module.icon || 'Star'} className=\"w-5 h-5 flex-shrink-0\" />\r\n {!isCollapsed && (\r\n <span className=\"font-medium\">{module.label}</span>\r\n )}\r\n {isCollapsed && (\r\n <div className=\"absolute left-full top-0 ml-2 px-3 py-2 bg-[var(--bg-secondary)] border border-[var(--border-color)] shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 whitespace-nowrap z-50\" style={{ borderRadius: 'var(--radius-menu-item)' }}>\r\n <span className=\"text-sm font-medium text-[var(--text-primary)]\">{module.label}</span>\r\n </div>\r\n )}\r\n </Link>\r\n );\r\n })}\r\n </nav>\r\n\r\n {/* Toggle button */}\r\n <div className={`border-t border-[var(--border-color)] ${isCollapsed ? 'p-2' : 'p-3'}`}>\r\n <button\r\n onClick={toggleCollapsed}\r\n className={`hidden lg:flex w-full items-center transition-all text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] ${\r\n isCollapsed ? 'justify-center px-2 py-2' : 'gap-2 px-3 py-2'\r\n }`}\r\n style={{ borderRadius: 'var(--radius-menu-item)' }}\r\n title={isCollapsed ? t('sidebar.expand', 'Agrandir le menu') : t('sidebar.collapse', 'Réduire le menu')}\r\n >\r\n {isCollapsed ? (\r\n <PanelLeft className=\"w-5 h-5\" />\r\n ) : (\r\n <>\r\n <PanelLeftClose className=\"w-5 h-5\" />\r\n <span className=\"text-sm\">{t('sidebar.collapse', 'Réduire')}</span>\r\n </>\r\n )}\r\n </button>\r\n </div>\r\n </div>\r\n );\r\n\r\n return (\r\n <>\r\n <aside className={`hidden lg:block h-[calc(100vh-4rem)] fixed top-16 left-0 bg-[var(--bg-secondary)] border-r border-[var(--border-color)] transition-all duration-300 ${\r\n isCollapsed ? 'w-16' : 'w-64'\r\n }`}>\r\n {sidebarContent}\r\n </aside>\r\n\r\n {isOpen && (\r\n <>\r\n <button\r\n type=\"button\"\r\n className=\"lg:hidden fixed inset-0 bg-black/50 z-40\"\r\n onClick={onClose}\r\n aria-label=\"Close sidebar\"\r\n />\r\n <aside className=\"lg:hidden fixed top-0 left-0 h-full w-64 bg-[var(--bg-secondary)] border-r border-[var(--border-color)] z-50\">\r\n {sidebarContent}\r\n </aside>\r\n </>\r\n )}\r\n </>\r\n );\r\n}\r\n","import { useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { ErrorBoundary, type FallbackProps } from 'react-error-boundary';\r\nimport { AlertTriangle, RefreshCw, ArrowLeft, Copy, Check, Bug, WifiOff, ShieldX, FileWarning } from 'lucide-react';\r\nimport { logService } from '@/services/logging/logService';\r\n\r\ninterface RouteErrorBoundaryProps {\r\n readonly children: React.ReactNode;\r\n}\r\n\r\ntype ErrorCategory = 'network' | 'permission' | 'notFound' | 'validation' | 'technical';\r\n\r\nfunction categorizeError(error: Error): ErrorCategory {\r\n const message = error.message.toLowerCase();\r\n const name = error.name.toLowerCase();\r\n\r\n if (message.includes('network') || message.includes('fetch') || message.includes('failed to fetch') || message.includes('econnrefused') || message.includes('timeout')) {\r\n return 'network';\r\n }\r\n\r\n if (message.includes('401') || message.includes('403') || message.includes('unauthorized') || message.includes('forbidden') || message.includes('permission')) {\r\n return 'permission';\r\n }\r\n\r\n if (message.includes('404') || message.includes('not found')) {\r\n return 'notFound';\r\n }\r\n\r\n if (name.includes('validation') || message.includes('validation') || message.includes('invalid')) {\r\n return 'validation';\r\n }\r\n\r\n return 'technical';\r\n}\r\n\r\nfunction generateErrorId(): string {\r\n const timestamp = Date.now().toString(36);\r\n const random = Math.random().toString(36).substring(2, 6);\r\n return `ERR-${timestamp}-${random}`.toUpperCase();\r\n}\r\n\r\nconst categoryConfig: Record<ErrorCategory, { icon: typeof AlertTriangle; color: string; bgColor: string }> = {\r\n network: { icon: WifiOff, color: 'text-orange-500', bgColor: 'bg-orange-500/10' },\r\n permission: { icon: ShieldX, color: 'text-red-500', bgColor: 'bg-red-500/10' },\r\n notFound: { icon: FileWarning, color: 'text-yellow-500', bgColor: 'bg-yellow-500/10' },\r\n validation: { icon: AlertTriangle, color: 'text-amber-500', bgColor: 'bg-amber-500/10' },\r\n technical: { icon: Bug, color: 'text-red-500', bgColor: 'bg-red-500/10' },\r\n};\r\n\r\nfunction RouteErrorFallback({ error, resetErrorBoundary }: FallbackProps) {\r\n const { t } = useTranslation('common');\r\n const [copied, setCopied] = useState(false);\r\n const [errorId] = useState(() => generateErrorId());\r\n\r\n const category = categorizeError(error);\r\n const config = categoryConfig[category];\r\n const Icon = config.icon;\r\n\r\n const handleCopyError = async () => {\r\n const errorDetails = `\r\nError ID: ${errorId}\r\nCategory: ${category}\r\nName: ${error.name}\r\nMessage: ${error.message}\r\nStack: ${error.stack || 'N/A'}\r\nURL: ${window.location.href}\r\nTimestamp: ${new Date().toISOString()}\r\n `.trim();\r\n\r\n try {\r\n await navigator.clipboard.writeText(errorDetails);\r\n setCopied(true);\r\n setTimeout(() => setCopied(false), 2000);\r\n } catch {\r\n console.error('Failed to copy error details');\r\n }\r\n };\r\n\r\n const handleGoBack = () => {\r\n // Navigate back with a full page reload to reset React state\r\n // Using navigate(-1) alone doesn't reset the error boundary\r\n if (window.history.length > 2 && document.referrer) {\r\n window.location.href = document.referrer;\r\n } else if (window.history.length > 2) {\r\n window.history.back();\r\n setTimeout(() => window.location.reload(), 50);\r\n } else {\r\n window.location.href = '/';\r\n }\r\n };\r\n\r\n return (\r\n <div className=\"flex items-center justify-center min-h-[400px] p-6\">\r\n <div className=\"max-w-md w-full p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-xl shadow-lg\">\r\n <div className=\"flex flex-col items-center text-center\">\r\n <div className={`w-14 h-14 rounded-full ${config.bgColor} flex items-center justify-center mb-4`}>\r\n <Icon className={`w-7 h-7 ${config.color}`} />\r\n </div>\r\n\r\n <h2 className=\"text-xl font-bold text-[var(--text-primary)] mb-2\">\r\n {t(`errors.${category}.title`, { defaultValue: t('errors.technical.title') })}\r\n </h2>\r\n\r\n <p className=\"text-sm text-[var(--text-secondary)] mb-3\">\r\n {t(`errors.${category}.description`, { defaultValue: t('errors.technical.description') })}\r\n </p>\r\n\r\n <div className=\"mb-4 px-2 py-1 bg-[var(--bg-tertiary)] rounded border border-[var(--border-color)]\">\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">{t('errors.errorId')}: </span>\r\n <span className=\"text-xs font-mono text-[var(--text-secondary)]\">{errorId}</span>\r\n </div>\r\n\r\n {import.meta.env.DEV && (\r\n <div className=\"w-full mb-4 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-left\">\r\n <div className=\"flex items-center justify-between mb-1\">\r\n <span className=\"text-xs font-semibold text-red-400\">{error.name}</span>\r\n <button\r\n onClick={handleCopyError}\r\n className=\"flex items-center gap-1 px-1.5 py-0.5 text-xs rounded bg-red-500/20 text-red-400 hover:bg-red-500/30 transition-colors\"\r\n >\r\n {copied ? <Check className=\"w-3 h-3\" /> : <Copy className=\"w-3 h-3\" />}\r\n {copied ? t('errors.copied') : t('errors.copyDetails')}\r\n </button>\r\n </div>\r\n <p className=\"text-xs font-mono text-red-500 break-all\">{error.message}</p>\r\n </div>\r\n )}\r\n\r\n <div className=\"flex gap-2 w-full\">\r\n <button\r\n onClick={resetErrorBoundary}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-2.5 px-3 rounded-lg bg-[var(--color-accent-600)] hover:bg-[var(--color-accent-700)] text-white text-sm font-medium transition-all\"\r\n >\r\n <RefreshCw className=\"w-4 h-4\" />\r\n {t('errors.retry')}\r\n </button>\r\n\r\n <button\r\n onClick={handleGoBack}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-2.5 px-3 rounded-lg bg-[var(--bg-tertiary)] hover:bg-[var(--bg-hover)] border border-[var(--border-color)] text-[var(--text-primary)] text-sm font-medium transition-all\"\r\n >\r\n <ArrowLeft className=\"w-4 h-4\" />\r\n {t('errors.goBack')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction handleRouteError(error: Error, info: React.ErrorInfo) {\r\n logService.logError(error, 'RouteErrorBoundary', {\r\n componentStack: info.componentStack\r\n });\r\n}\r\n\r\nexport function RouteErrorBoundary({ children }: RouteErrorBoundaryProps): ReactElement {\r\n return (\r\n <ErrorBoundary\r\n FallbackComponent={RouteErrorFallback}\r\n onError={handleRouteError}\r\n onReset={() => {\r\n window.location.reload();\r\n }}\r\n >\r\n {children}\r\n </ErrorBoundary>\r\n );\r\n}\r\n","import { useLicense } from '@/contexts/LicenseContext';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Lock, Sparkles } from 'lucide-react';\r\nimport type { ReactNode, ReactElement } from 'react';\r\n\r\ninterface Props {\r\n /** Feature name to check (e.g., 'ai', 'workflows', 'support') */\r\n readonly feature: string;\r\n /** Content to render if feature is enabled */\r\n readonly children: ReactNode;\r\n /** Custom fallback content (optional) */\r\n readonly fallback?: ReactNode;\r\n /** Show a minimal locked indicator instead of full card */\r\n readonly minimal?: boolean;\r\n}\r\n\r\n/**\r\n * Conditionally renders children based on license feature availability.\r\n * If feature is not enabled, shows a locked placeholder or custom fallback.\r\n */\r\nexport function FeatureGate({ feature, children, fallback, minimal = false }: Props): ReactElement {\r\n const { t } = useTranslation('admin');\r\n const { isFeatureEnabled, license, isLoading } = useLicense();\r\n\r\n // While loading, show children to prevent flash\r\n if (isLoading) {\r\n return <>{children}</>;\r\n }\r\n\r\n // Feature is enabled\r\n if (isFeatureEnabled(feature)) {\r\n return <>{children}</>;\r\n }\r\n\r\n // Custom fallback provided\r\n if (fallback) {\r\n return <>{fallback}</>;\r\n }\r\n\r\n // Minimal locked indicator\r\n if (minimal) {\r\n return (\r\n <div className=\"inline-flex items-center gap-1.5 px-2 py-1 bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 rounded text-sm\">\r\n <Lock className=\"w-3.5 h-3.5\" />\r\n <span>{t('license.featureLocked', 'Locked')}</span>\r\n </div>\r\n );\r\n }\r\n\r\n // Full locked placeholder\r\n return (\r\n <div className=\"flex flex-col items-center justify-center p-8 bg-gray-50 dark:bg-gray-800/50 rounded-xl border-2 border-dashed border-gray-200 dark:border-gray-700\">\r\n <div className=\"p-4 bg-gray-100 dark:bg-gray-700 rounded-full mb-4\">\r\n <Lock className=\"w-8 h-8 text-gray-400 dark:text-gray-500\" />\r\n </div>\r\n\r\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-white mb-2\">\r\n {t('license.featureNotAvailable', 'Feature Not Available')}\r\n </h3>\r\n\r\n <p className=\"text-sm text-gray-500 dark:text-gray-400 text-center max-w-sm mb-4\">\r\n {t('license.upgradeToAccess', 'Upgrade your license to access the {{feature}} feature.', { feature })}\r\n </p>\r\n\r\n <div className=\"flex items-center gap-2 text-xs text-gray-400 dark:text-gray-500\">\r\n <Sparkles className=\"w-4 h-4\" />\r\n <span>\r\n {t('license.currentEdition', 'Current: {{edition}}', { edition: license?.edition || 'Unknown' })}\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n/**\r\n * Hook version for programmatic feature checking\r\n */\r\nexport function useFeatureGate(feature: string): {\r\n isEnabled: boolean;\r\n isLoading: boolean;\r\n edition: string | null;\r\n} {\r\n const { isFeatureEnabled, license, isLoading } = useLicense();\r\n\r\n return {\r\n isEnabled: isFeatureEnabled(feature),\r\n isLoading,\r\n edition: license?.edition || null,\r\n };\r\n}\r\n","import { useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useLicense } from '@/contexts/LicenseContext';\r\nimport { useNavigation } from '@/contexts/NavigationContext';\r\nimport { Key, Loader2, X, CheckCircle, AlertCircle } from 'lucide-react';\r\n\r\ninterface Props {\r\n readonly open: boolean;\r\n readonly onOpenChange: (open: boolean) => void;\r\n}\r\n\r\nexport function LicenseActivationDialog({ open, onOpenChange }: Props): ReactElement | null {\r\n const { t } = useTranslation('admin');\r\n const { activateLicense, license } = useLicense();\r\n const { refreshMenu } = useNavigation();\r\n const [licenseKey, setLicenseKey] = useState('');\r\n const [apiKey, setApiKey] = useState('');\r\n const [showApiKey, setShowApiKey] = useState(false);\r\n const [isActivating, setIsActivating] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const [success, setSuccess] = useState(false);\r\n\r\n const hasExistingApiKey = license?.hasApiKey ?? false;\r\n\r\n const handleActivate = async () => {\r\n if (!licenseKey.trim()) {\r\n setError(t('license.errors.keyRequired', 'License key is required'));\r\n return;\r\n }\r\n\r\n setIsActivating(true);\r\n setError(null);\r\n setSuccess(false);\r\n\r\n const result = await activateLicense(licenseKey.trim(), apiKey.trim() || undefined);\r\n\r\n setIsActivating(false);\r\n\r\n if (result.success) {\r\n setSuccess(true);\r\n // Refresh navigation menu to reflect new license features/modules\r\n await refreshMenu();\r\n setTimeout(() => {\r\n setLicenseKey('');\r\n setApiKey('');\r\n setShowApiKey(false);\r\n setSuccess(false);\r\n onOpenChange(false);\r\n }, 1500);\r\n } else {\r\n setError(result.error || t('license.errors.activationFailed', 'Activation failed'));\r\n }\r\n };\r\n\r\n const handleClose = () => {\r\n if (!isActivating) {\r\n setLicenseKey('');\r\n setApiKey('');\r\n setShowApiKey(false);\r\n setError(null);\r\n setSuccess(false);\r\n onOpenChange(false);\r\n }\r\n };\r\n\r\n if (!open) return null;\r\n\r\n return (\r\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\r\n {/* Backdrop */}\r\n <button\r\n type=\"button\"\r\n className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\"\r\n onClick={handleClose}\r\n aria-label=\"Close dialog\"\r\n />\r\n\r\n {/* Dialog */}\r\n <div className=\"relative bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-lg mx-4 overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 bg-blue-100 dark:bg-blue-900/30 rounded-lg\">\r\n <Key className=\"w-5 h-5 text-blue-600 dark:text-blue-400\" />\r\n </div>\r\n <h2 className=\"text-lg font-semibold text-gray-900 dark:text-white\">\r\n {t('license.activateTitle', 'Activate License')}\r\n </h2>\r\n </div>\r\n <button\r\n onClick={handleClose}\r\n disabled={isActivating}\r\n className=\"p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50\"\r\n >\r\n <X className=\"w-5 h-5\" />\r\n </button>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"px-6 py-4 space-y-4\">\r\n <p className=\"text-sm text-gray-600 dark:text-gray-400\">\r\n {t('license.activateDescription', 'Enter your license key to activate the product. The key is a JWT token provided by AtlasHub.')}\r\n </p>\r\n\r\n <div>\r\n <label className=\"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2\">\r\n {t('license.keyLabel', 'License Key')}\r\n </label>\r\n <textarea\r\n value={licenseKey}\r\n onChange={(e) => {\r\n setLicenseKey(e.target.value);\r\n setError(null);\r\n }}\r\n placeholder={t('license.keyPlaceholder', 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...')}\r\n rows={6}\r\n disabled={isActivating || success}\r\n className=\"w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg font-mono text-xs bg-white dark:bg-gray-900 text-gray-900 dark:text-white placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed resize-none\"\r\n />\r\n </div>\r\n\r\n {hasExistingApiKey && !showApiKey ? (\r\n <div className=\"flex items-center gap-2 p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg\">\r\n <CheckCircle className=\"w-4 h-4 text-green-600 dark:text-green-400 flex-shrink-0\" />\r\n <span className=\"text-sm text-green-700 dark:text-green-300 flex-1\">\r\n {t('license.apiKeyConfigured', 'API key is already configured')}\r\n </span>\r\n <button\r\n type=\"button\"\r\n onClick={() => setShowApiKey(true)}\r\n className=\"text-xs text-blue-600 dark:text-blue-400 hover:underline\"\r\n >\r\n {t('license.apiKeyChange', 'Change')}\r\n </button>\r\n </div>\r\n ) : (\r\n <div>\r\n <label className=\"block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2\">\r\n {t('license.apiKeyLabel', 'API Key')}\r\n </label>\r\n <input\r\n type=\"text\"\r\n value={apiKey}\r\n onChange={(e) => {\r\n setApiKey(e.target.value);\r\n setError(null);\r\n }}\r\n placeholder={t('license.apiKeyPlaceholder', 'sslk_...')}\r\n disabled={isActivating || success}\r\n className=\"w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg font-mono text-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-white placeholder-gray-400 focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed\"\r\n />\r\n <p className=\"mt-1 text-xs text-gray-500 dark:text-gray-400\">\r\n {t('license.apiKeyDescription', 'API key provided by AtlasHub for server authentication (sslk_...).')}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {/* Error message */}\r\n {error && (\r\n <div className=\"flex items-start gap-2 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg\">\r\n <AlertCircle className=\"w-5 h-5 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5\" />\r\n <p className=\"text-sm text-red-700 dark:text-red-300\">{error}</p>\r\n </div>\r\n )}\r\n\r\n {/* Success message */}\r\n {success && (\r\n <div className=\"flex items-start gap-2 p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg\">\r\n <CheckCircle className=\"w-5 h-5 text-green-600 dark:text-green-400 flex-shrink-0 mt-0.5\" />\r\n <p className=\"text-sm text-green-700 dark:text-green-300\">\r\n {t('license.activationSuccess', 'License activated successfully!')}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {/* Contact help */}\r\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">\r\n {t('license.activationHelp', 'Need a license key? Contact us at')}{' '}\r\n <a href=\"mailto:support@atlashub.ch\" className=\"text-blue-600 dark:text-blue-400 hover:underline\">\r\n support@atlashub.ch\r\n </a>\r\n {' '}{t('license.activationHelpOr', 'or visit')}{' '}\r\n <a href=\"https://www.atlashub.ch\" target=\"_blank\" rel=\"noopener noreferrer\" className=\"text-blue-600 dark:text-blue-400 hover:underline\">\r\n www.atlashub.ch\r\n </a>\r\n </p>\r\n </div>\r\n\r\n {/* Footer */}\r\n <div className=\"flex justify-end gap-3 px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50\">\r\n <button\r\n onClick={handleClose}\r\n disabled={isActivating}\r\n className=\"px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\r\n >\r\n {t('common:cancel', 'Cancel')}\r\n </button>\r\n <button\r\n onClick={handleActivate}\r\n disabled={isActivating || success || !licenseKey.trim()}\r\n className=\"px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center gap-2\"\r\n >\r\n {isActivating && <Loader2 className=\"w-4 h-4 animate-spin\" />}\r\n {success && <CheckCircle className=\"w-4 h-4\" />}\r\n {(() => {\r\n if (isActivating) return t('license.activating', 'Activating...');\r\n if (success) return t('license.activated', 'Activated!');\r\n return t('license.activate', 'Activate');\r\n })()}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { useLicense } from '@/contexts/LicenseContext';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { AlertTriangle, CheckCircle, Clock, XCircle, Shield } from 'lucide-react';\r\n\r\ninterface Props {\r\n readonly showDetails?: boolean;\r\n}\r\n\r\nexport function LicenseStatusBadge({ showDetails = false }: Props): ReactElement | null {\r\n const { t } = useTranslation('admin');\r\n const { license, isLoading } = useLicense();\r\n\r\n if (isLoading || !license) return null;\r\n\r\n const { status, edition, isInTrial, trialDaysRemaining, isReadOnlyMode, daysUntilExpiration } = license;\r\n\r\n // Read-only mode (most critical)\r\n if (isReadOnlyMode) {\r\n return (\r\n <div className=\"flex items-center gap-2 px-3 py-1.5 bg-red-100 dark:bg-red-900/30 text-red-800 dark:text-red-200 rounded-full text-sm font-medium\">\r\n <XCircle className=\"w-4 h-4\" />\r\n <span>{t('license.readOnlyMode', 'Read-only mode')}</span>\r\n </div>\r\n );\r\n }\r\n\r\n // Trial mode\r\n if (isInTrial) {\r\n const urgency = trialDaysRemaining <= 7 ? 'text-amber-800 dark:text-amber-200 bg-amber-100 dark:bg-amber-900/30'\r\n : 'text-blue-800 dark:text-blue-200 bg-blue-100 dark:bg-blue-900/30';\r\n return (\r\n <div className={`flex items-center gap-2 px-3 py-1.5 rounded-full text-sm font-medium ${urgency}`}>\r\n <Clock className=\"w-4 h-4\" />\r\n <span>\r\n {t('license.trialDaysRemaining', '{{days}} trial days left', { days: trialDaysRemaining })}\r\n </span>\r\n </div>\r\n );\r\n }\r\n\r\n // License expiring soon (30 days or less)\r\n if (daysUntilExpiration <= 30 && status === 'Active') {\r\n return (\r\n <div className=\"flex items-center gap-2 px-3 py-1.5 bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-200 rounded-full text-sm font-medium\">\r\n <AlertTriangle className=\"w-4 h-4\" />\r\n <span>\r\n {t('license.expiresIn', 'Expires in {{days}} days', { days: daysUntilExpiration })}\r\n </span>\r\n </div>\r\n );\r\n }\r\n\r\n // Active license\r\n if (status === 'Active') {\r\n return (\r\n <div className=\"flex items-center gap-2 px-3 py-1.5 bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-200 rounded-full text-sm font-medium\">\r\n <CheckCircle className=\"w-4 h-4\" />\r\n <span>{t(`license.edition.${edition.toLowerCase()}`, { defaultValue: edition })}</span>\r\n {showDetails && (\r\n <span className=\"text-xs opacity-75\">\r\n ({daysUntilExpiration} {t('license.daysLeft', 'days left')})\r\n </span>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n // Expired or Revoked\r\n return (\r\n <div className=\"flex items-center gap-2 px-3 py-1.5 bg-red-100 dark:bg-red-900/30 text-red-800 dark:text-red-200 rounded-full text-sm font-medium\">\r\n <Shield className=\"w-4 h-4\" />\r\n <span>{t(`license.status.${status.toLowerCase()}`, { defaultValue: status })}</span>\r\n </div>\r\n );\r\n}\r\n","import { useMemo } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useLocation } from 'react-router-dom';\r\nimport { ShieldX, ArrowLeft, Home, LayoutGrid, TicketPlus } from 'lucide-react';\r\nimport { useNavigation } from '@/contexts/NavigationContext';\r\n\r\ntype DenialReason = 'tenant' | 'permission';\r\n\r\ninterface AccessDeniedProps {\r\n /** Why access was denied: 'tenant' = app not available for workspace, 'permission' = user lacks permissions */\r\n readonly reason?: DenialReason;\r\n /** Tenant name to display in the error message */\r\n readonly tenantName?: string;\r\n /** Permission path required to access the page (e.g. \"administration.users\") */\r\n readonly permissionPath?: string;\r\n /** URL to the applications list page */\r\n readonly applicationsUrl?: string;\r\n /** User's current roles */\r\n readonly userRoles?: string[];\r\n /** User's permission paths (all or filtered) */\r\n readonly userPermissions?: string[];\r\n}\r\n\r\nexport function AccessDenied({\r\n reason,\r\n tenantName,\r\n permissionPath,\r\n applicationsUrl,\r\n userRoles,\r\n userPermissions,\r\n}: AccessDeniedProps): ReactElement {\r\n const { t } = useTranslation('common');\r\n const { menu } = useNavigation();\r\n const location = useLocation();\r\n\r\n const handleGoBack = () => {\r\n if (window.history.length > 2) {\r\n window.history.back();\r\n } else {\r\n window.location.href = '/';\r\n }\r\n };\r\n\r\n // Extract the app code (first segment) from permission path for filtering\r\n const appCode = permissionPath?.split('.')[0];\r\n\r\n // Filter permissions relevant to the requested app (including global wildcard)\r\n const relevantPermissions = useMemo(() =>\r\n userPermissions?.filter(p => {\r\n if (p === '*') return true;\r\n const lower = p.toLowerCase();\r\n return appCode && (lower.startsWith(appCode.toLowerCase() + '.') || lower === appCode.toLowerCase());\r\n }) ?? [],\r\n [userPermissions, appCode],\r\n );\r\n\r\n const hasDetails = permissionPath && (userRoles?.length || userPermissions);\r\n\r\n // Resolve human-readable navigation labels from the menu API + URL path\r\n const navigationLabels = useMemo(() => {\r\n const parts = location.pathname.split('/').filter(Boolean);\r\n const segments = parts[0] === 't' ? parts.slice(2) : parts;\r\n\r\n const appSeg = segments[0];\r\n const moduleSeg = segments[1];\r\n const sectionSeg = segments[2];\r\n const resourceSeg = segments[3];\r\n\r\n const isUuid = (s: string) => /^[0-9a-f]{8}-[0-9a-f]{4}-/i.test(s);\r\n const stripPrefix = (code: string) => code.replace(/^(core_|ext_)/, '');\r\n const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1).replace(/-/g, ' ');\r\n\r\n const app = menu?.applications.find(a => {\r\n const seg = a.route?.split('/').filter(Boolean)[0];\r\n return seg === appSeg || stripPrefix(a.code) === appSeg;\r\n });\r\n\r\n const mod = moduleSeg ? app?.modules.find(m => {\r\n const seg = m.route?.split('/').filter(Boolean)[1];\r\n return seg === moduleSeg || stripPrefix(m.code) === moduleSeg;\r\n }) : undefined;\r\n\r\n const sec = sectionSeg && !isUuid(sectionSeg) ? mod?.sections.find(s => {\r\n const seg = s.route?.split('/').filter(Boolean)[2];\r\n return seg === sectionSeg || stripPrefix(s.code) === sectionSeg;\r\n }) : undefined;\r\n\r\n const res = resourceSeg && !isUuid(resourceSeg) ? sec?.resources.find(r => {\r\n const seg = r.route?.split('/').filter(Boolean)[3];\r\n return seg === resourceSeg || stripPrefix(r.code) === resourceSeg;\r\n }) : undefined;\r\n\r\n return {\r\n application: app?.label || (appSeg ? capitalize(appSeg) : undefined),\r\n module: mod?.label || (moduleSeg && !isUuid(moduleSeg) ? capitalize(moduleSeg) : undefined),\r\n section: sec?.label || (sectionSeg && !isUuid(sectionSeg) ? capitalize(sectionSeg) : undefined),\r\n resource: res?.label || (resourceSeg && !isUuid(resourceSeg) ? capitalize(resourceSeg) : undefined),\r\n };\r\n }, [menu, location.pathname]);\r\n\r\n // Build navigation context string for the ticket body\r\n const navigationContext = useMemo(() => {\r\n const lines: string[] = [];\r\n if (navigationLabels.application)\r\n lines.push(`- ${t('errors.accessDenied.ticket.applicationLabel')}: ${navigationLabels.application}`);\r\n if (navigationLabels.module)\r\n lines.push(`- ${t('errors.accessDenied.ticket.moduleLabel')}: ${navigationLabels.module}`);\r\n if (navigationLabels.section)\r\n lines.push(`- ${t('errors.accessDenied.ticket.sectionLabel')}: ${navigationLabels.section}`);\r\n if (navigationLabels.resource)\r\n lines.push(`- ${t('errors.accessDenied.ticket.resourceLabel')}: ${navigationLabels.resource}`);\r\n if (lines.length === 0) return '';\r\n return `\\n\\n${t('errors.accessDenied.ticket.navigationContext')}:\\n${lines.join('\\n')}`;\r\n }, [navigationLabels, t]);\r\n\r\n // Build ticket creation URL with pre-filled context (translated)\r\n const ticketUrl = useMemo(() => {\r\n if (!reason || !permissionPath || !tenantName || !applicationsUrl) return null;\r\n\r\n const base = applicationsUrl.replace(/\\/applications$/, '');\r\n const createUrl = `${base}/support/my-tickets/create`;\r\n const type = reason === 'tenant' ? 'ApplicationRequest' : 'Support';\r\n const appCode_ = permissionPath.split('.')[0];\r\n\r\n const subject = reason === 'tenant'\r\n ? t('errors.accessDenied.ticket.accessSubject', { permissionPath, tenantName })\r\n : t('errors.accessDenied.ticket.permissionSubject', { permissionPath, tenantName });\r\n const body = reason === 'tenant'\r\n ? t('errors.accessDenied.ticket.accessBody', { appCode: appCode_, tenantName, permissionPath })\r\n : t('errors.accessDenied.ticket.permissionBody', { permissionPath, tenantName });\r\n const fullBody = body + navigationContext;\r\n\r\n const params = new URLSearchParams({ type, subject, body: fullBody });\r\n return `${createUrl}?${params.toString()}`;\r\n }, [reason, permissionPath, tenantName, applicationsUrl, t, navigationContext]);\r\n\r\n // Pick the right description based on reason\r\n const getDescriptionText = () => {\r\n if (reason === 'tenant' && tenantName) return t('errors.accessDenied.tenantNotAvailable', { tenantName });\r\n if (reason === 'permission' && tenantName) return t('errors.accessDenied.permissionMissing', { tenantName });\r\n if (tenantName) return t('errors.accessDenied.tenantDescription', { tenantName });\r\n return t('errors.accessDenied.description');\r\n };\r\n const descriptionText = getDescriptionText();\r\n\r\n return (\r\n <div className=\"min-h-[60vh] flex items-center justify-center p-4\">\r\n <div className=\"max-w-lg w-full p-8 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-2xl shadow-xl\">\r\n <div className=\"flex flex-col items-center text-center\">\r\n <div className=\"w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center mb-6\">\r\n <ShieldX className=\"w-8 h-8 text-red-500\" />\r\n </div>\r\n\r\n <h1 className=\"text-2xl font-bold text-[var(--text-primary)] mb-2\">\r\n {t('errors.accessDenied.title')}\r\n </h1>\r\n\r\n <p className=\"text-[var(--text-secondary)] mb-4\">\r\n {descriptionText}\r\n </p>\r\n\r\n {hasDetails && (\r\n <div className=\"w-full mb-6 bg-[var(--bg-secondary)] rounded-lg border border-[var(--border-color)] divide-y divide-[var(--border-color)]\">\r\n {/* Required permission */}\r\n <div className=\"p-3\">\r\n <p className=\"text-xs text-[var(--text-tertiary)] mb-1\">\r\n {t('errors.accessDenied.requiredPermission')}\r\n </p>\r\n <p className=\"text-sm font-mono font-medium text-red-500\">\r\n {permissionPath}\r\n </p>\r\n </div>\r\n\r\n {/* User roles */}\r\n {(userRoles?.length ?? 0) > 0 && (\r\n <div className=\"p-3\">\r\n <p className=\"text-xs text-[var(--text-tertiary)] mb-2\">\r\n {t('errors.accessDenied.yourRoles')}\r\n </p>\r\n <div className=\"flex flex-wrap gap-1.5 justify-center\">\r\n {userRoles?.map(role => (\r\n <span\r\n key={role}\r\n className=\"inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium bg-[var(--bg-tertiary)] text-[var(--text-secondary)] border border-[var(--border-color)]\"\r\n >\r\n {role}\r\n </span>\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* User permissions for this app */}\r\n {userPermissions && (\r\n <div className=\"p-3\">\r\n <p className=\"text-xs text-[var(--text-tertiary)] mb-2\">\r\n {t('errors.accessDenied.yourPermissionsFor', { appCode: appCode ?? '' })}\r\n </p>\r\n {relevantPermissions.length > 0 ? (\r\n <div className=\"flex flex-col gap-1 items-start\">\r\n {relevantPermissions.map(perm => (\r\n <span key={perm} className=\"text-xs font-mono text-[var(--text-secondary)]\">\r\n {perm}\r\n </span>\r\n ))}\r\n </div>\r\n ) : (\r\n <p className=\"text-xs text-[var(--text-tertiary)] italic\">\r\n {t('errors.accessDenied.noPermissions')}\r\n </p>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n {!hasDetails && permissionPath && (\r\n <div className=\"w-full mb-6 p-3 bg-[var(--bg-secondary)] rounded-lg border border-[var(--border-color)]\">\r\n <p className=\"text-xs text-[var(--text-tertiary)] mb-1\">\r\n {t('errors.accessDenied.requiredPermission')}\r\n </p>\r\n <p className=\"text-sm font-mono text-[var(--text-primary)]\">\r\n {permissionPath}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <div className=\"flex flex-col gap-3 w-full\">\r\n {/* Primary action: open a ticket */}\r\n {ticketUrl && (\r\n <a\r\n href={ticketUrl}\r\n className=\"flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--color-accent-600)] hover:bg-[var(--color-accent-700)] text-white font-medium transition-all\"\r\n >\r\n <TicketPlus className=\"w-4 h-4\" />\r\n {reason === 'tenant'\r\n ? t('errors.accessDenied.requestAccess')\r\n : t('errors.accessDenied.requestPermission')}\r\n </a>\r\n )}\r\n\r\n {/* Secondary actions */}\r\n <div className=\"flex flex-col sm:flex-row gap-3 w-full\">\r\n {applicationsUrl ? (\r\n <a\r\n href={applicationsUrl}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-hover)] border border-[var(--border-color)] text-[var(--text-primary)] font-medium transition-all\"\r\n >\r\n <LayoutGrid className=\"w-4 h-4\" />\r\n {t('errors.accessDenied.goToApplications')}\r\n </a>\r\n ) : (\r\n <button\r\n onClick={handleGoBack}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-hover)] border border-[var(--border-color)] text-[var(--text-primary)] font-medium transition-all\"\r\n >\r\n <ArrowLeft className=\"w-4 h-4\" />\r\n {t('errors.goBack')}\r\n </button>\r\n )}\r\n\r\n <a\r\n href=\"/\"\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-hover)] border border-[var(--border-color)] text-[var(--text-primary)] font-medium transition-all\"\r\n >\r\n <Home className=\"w-4 h-4\" />\r\n {t('errors.home')}\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Building2, Mail, LogOut } from 'lucide-react';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\n\r\nexport function NoActiveWorkspace(): ReactElement {\r\n const { t } = useTranslation('common');\r\n const { logout } = useAuth();\r\n\r\n return (\r\n <div className=\"min-h-[60vh] flex items-center justify-center p-4\">\r\n <div className=\"max-w-lg w-full p-8 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-2xl shadow-xl\">\r\n <div className=\"flex flex-col items-center text-center\">\r\n <div className=\"w-16 h-16 rounded-full bg-orange-500/10 flex items-center justify-center mb-6\">\r\n <Building2 className=\"w-8 h-8 text-orange-500\" />\r\n </div>\r\n\r\n <h1 className=\"text-2xl font-bold text-[var(--text-primary)] mb-2\">\r\n {t('errors.noActiveWorkspace.title')}\r\n </h1>\r\n\r\n <p className=\"text-[var(--text-secondary)] mb-6\">\r\n {t('errors.noActiveWorkspace.description')}\r\n </p>\r\n\r\n <div className=\"flex flex-col sm:flex-row gap-3 w-full\">\r\n <a\r\n href=\"mailto:support@atlashub.ch\"\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--color-accent-600)] hover:bg-[var(--color-accent-700)] text-white font-medium transition-all\"\r\n >\r\n <Mail className=\"w-4 h-4\" />\r\n {t('errors.noActiveWorkspace.contactAdmin')}\r\n </a>\r\n\r\n <button\r\n onClick={() => void logout()}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-hover)] border border-[var(--border-color)] text-[var(--text-primary)] font-medium transition-all\"\r\n >\r\n <LogOut className=\"w-4 h-4\" />\r\n {t('errors.noActiveWorkspace.logout')}\r\n </button>\r\n </div>\r\n\r\n <p className=\"text-sm text-[var(--text-tertiary)] mt-6\">\r\n {t('errors.noActiveWorkspace.hint')}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { Loader2 } from 'lucide-react';\r\n\r\n/**\r\n * Full-page loading spinner for React.lazy Suspense fallback\r\n */\r\nexport function PageLoader(): ReactElement {\r\n return (\r\n <div className=\"flex items-center justify-center min-h-[400px] w-full\">\r\n <div className=\"flex flex-col items-center gap-4\">\r\n <Loader2 className=\"w-10 h-10 animate-spin text-[var(--color-primary-500)]\" />\r\n <span className=\"text-sm text-[var(--color-text-secondary)]\">\r\n Chargement...\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useRef } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { Navigate, Outlet, useLocation } from 'react-router-dom';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { useNavigation } from '@/contexts/NavigationContext';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { hasRouteAccess } from '@/utils/permissions';\r\nimport { SYSTEM_ROUTE_SEGMENTS } from '@/constants/routes';\r\nimport { AccessDenied } from '@/components/errors/AccessDenied';\r\nimport { NoActiveWorkspace } from '@/components/errors/NoActiveWorkspace';\r\nimport { PageLoader } from '@/components/ui/PageLoader';\r\nimport { AppHeader } from '@/components/layout/AppHeader';\r\nimport { api } from '@/services/api/apiClient';\r\n\r\n/**\r\n * API-driven route guard — replaces RouteGuard with dynamic configuration.\r\n *\r\n * Instead of hardcoded OPEN_APPS and PERMISSION_CHECKED_APPS lists,\r\n * uses the `isOpen` flag and `permissionPath` from the enriched menu API.\r\n *\r\n * Authorization flow:\r\n * 1. Not authenticated → redirect to /login\r\n * 2. Tenant switching in progress → show loader\r\n * 3. No active workspace → show NoActiveWorkspace\r\n * 4. Open app (isOpen=true from API) → pass through\r\n * 5. App not in tenant menu → AccessDenied\r\n * 6. App requires permission check → verify permission prefix → AccessDenied\r\n * 7. All checks passed → render child routes via <Outlet />\r\n */\r\nexport function ProtectedRoute(): ReactElement {\r\n const { user, isLoading: authLoading } = useAuth();\r\n const location = useLocation();\r\n const { currentAppCode, currentModuleCode, isLoading: navLoading, menu } = useNavigation();\r\n const { currentTenant, userTenants, isLoading: tenantLoading, isGlobalView } = useTenant();\r\n\r\n // Force remount of all child routes when tenant changes.\r\n const tenantKey = currentTenant?.id || 'global';\r\n\r\n // Fire-and-forget audit for route-level access denials (deduplicated per path)\r\n const lastAuditedPath = useRef<string | null>(null);\r\n const logAccessDenied = (reason: string, permissionPath: string) => {\r\n const key = `${reason}:${permissionPath}:${location.pathname}`;\r\n if (lastAuditedPath.current === key) return;\r\n lastAuditedPath.current = key;\r\n api.post('/security/access-denied', {\r\n reason,\r\n permissionPath,\r\n attemptedPath: location.pathname,\r\n }).catch(() => {});\r\n };\r\n\r\n // Detect tenant transition\r\n const prevTenantIdRef = useRef(currentTenant?.id);\r\n let tenantTransitioning = false;\r\n if (currentTenant?.id !== prevTenantIdRef.current) {\r\n prevTenantIdRef.current = currentTenant?.id;\r\n tenantTransitioning = true;\r\n }\r\n\r\n // Wait for auth to resolve\r\n if (authLoading) return <PageLoader />;\r\n\r\n // Not authenticated — redirect to login with return path\r\n if (!user) {\r\n return <Navigate to=\"/login\" state={{ from: location }} replace />;\r\n }\r\n\r\n // Block ALL routes during tenant transition or while menu is loading.\r\n // Also block when menu hasn't loaded yet (null) — prevents evaluating with stale data.\r\n if (tenantTransitioning || navLoading || tenantLoading || !menu) return <PageLoader />;\r\n\r\n // No active workspace\r\n if (!currentTenant && !isGlobalView) {\r\n const hasActiveTenants = userTenants.some(t => t.status === 'Active');\r\n if (!hasActiveTenants) {\r\n return <NoActiveWorkspace />;\r\n }\r\n }\r\n\r\n // No app detected in URL (root route) → pass through\r\n if (!currentAppCode) {\r\n return <Outlet key={tenantKey} />;\r\n }\r\n\r\n // System routes — not from DB navigation, always accessible to authenticated users.\r\n // These routes are defined in DynamicRouter as static system routes and handle\r\n // their own empty-state UI (e.g. \"no applications available\").\r\n if ((SYSTEM_ROUTE_SEGMENTS as readonly string[]).includes(currentAppCode)) {\r\n return <Outlet key={tenantKey} />;\r\n }\r\n\r\n // Find the current application in the menu (API-driven)\r\n const allApps = menu?.applications || [];\r\n const currentApp = allApps.find(app => {\r\n const appRouteSegment = app.route?.split('/').filter(Boolean)[0];\r\n return appRouteSegment === currentAppCode || app.code === currentAppCode;\r\n });\r\n\r\n // ── Open apps — use `isOpen` flag from API instead of hardcoded list ──\r\n if (currentApp?.isOpen) {\r\n return <Outlet key={tenantKey} />;\r\n }\r\n\r\n // ── App not in menu (tenant doesn't have access) ──\r\n if (!currentApp) {\r\n const permissionPrefix = [currentAppCode, currentModuleCode].filter(Boolean).join('.');\r\n const applicationsUrl = currentTenant?.slug ? `/t/${currentTenant.slug}/applications` : '/applications';\r\n logAccessDenied('tenant', permissionPrefix);\r\n\r\n return (\r\n <div className=\"min-h-screen bg-[var(--bg-primary)]\">\r\n <AppHeader onMenuToggle={() => {}} />\r\n <main className=\"pt-16 transition-all duration-300 h-[calc(100vh-4rem)]\">\r\n <div className=\"p-4 sm:p-6 lg:px-10 h-full overflow-auto\">\r\n <AccessDenied\r\n reason=\"tenant\"\r\n tenantName={currentTenant?.name}\r\n permissionPath={permissionPrefix}\r\n applicationsUrl={applicationsUrl}\r\n userRoles={user.roles}\r\n userPermissions={user.permissions}\r\n />\r\n </div>\r\n </main>\r\n </div>\r\n );\r\n }\r\n\r\n // ── Permission check using permissionPath from API ──\r\n // Build the permission prefix from the current navigation context\r\n const prefix = [currentAppCode, currentModuleCode]\r\n .filter(Boolean)\r\n .join('.');\r\n\r\n if (!hasRouteAccess(user.permissions, prefix)) {\r\n // RequestAccess level bypasses the permission guard\r\n if (currentApp.accessLevel !== 'RequestAccess') {\r\n const applicationsUrl = currentTenant?.slug ? `/t/${currentTenant.slug}/applications` : '/applications';\r\n logAccessDenied('permission', prefix);\r\n\r\n return (\r\n <div className=\"min-h-screen bg-[var(--bg-primary)]\">\r\n <AppHeader onMenuToggle={() => {}} />\r\n <main className=\"pt-16 transition-all duration-300 h-[calc(100vh-4rem)]\">\r\n <div className=\"p-4 sm:p-6 lg:px-10 h-full overflow-auto\">\r\n <AccessDenied\r\n reason=\"permission\"\r\n tenantName={currentTenant?.name}\r\n permissionPath={prefix}\r\n applicationsUrl={applicationsUrl}\r\n userRoles={user.roles}\r\n userPermissions={user.permissions}\r\n />\r\n </div>\r\n </main>\r\n </div>\r\n );\r\n }\r\n }\r\n\r\n // All checks passed — render the page\r\n return <Outlet key={tenantKey} />;\r\n}\r\n","/**\r\n * Tenant Route Wrapper\r\n *\r\n * Synchronizes URL tenant slug with TenantContext.\r\n * Handles redirects for multi-tenant vs single-tenant modes.\r\n *\r\n * Behaviors:\r\n * - Multi-tenant mode: URLs include /t/{slug}/ prefix\r\n * - Single-tenant mode: Standard URLs without prefix\r\n */\r\n\r\nimport { useEffect, useRef } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { Outlet, useParams, useNavigate, useLocation } from 'react-router-dom';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { useFeatureConfig } from '@/contexts/FeatureConfigContext';\r\nimport { extractSlugFromPath, stripSlugFromPath } from '@/utils/tenantUrl';\r\n\r\nexport function TenantRouteWrapper(): ReactElement {\r\n const { slug } = useParams<{ slug?: string }>();\r\n const navigate = useNavigate();\r\n const location = useLocation();\r\n const { currentTenant, userTenants, switchTenant, isLoading, isGlobalView } = useTenant();\r\n const { useUrlRouting, isLoading: configLoading } = useFeatureConfig();\r\n\r\n // Track if we're currently processing a navigation to avoid loops\r\n const isNavigating = useRef(false);\r\n // Track the tenant we're switching TO (to avoid redirect conflicts)\r\n const pendingSwitchSlug = useRef<string | null>(null);\r\n // Track the previous URL slug to detect actual URL changes vs state changes\r\n // This prevents Effect 1 from reverting programmatic tenant switches\r\n const prevUrlSlugRef = useRef<string | null>(null);\r\n\r\n // Effect 1: Sync URL slug to TenantContext\r\n // IMPORTANT: Only runs when URL actually changes (user navigation/typing URL),\r\n // NOT when state changes (programmatic switch via TenantSelector)\r\n useEffect(() => {\r\n // Wait for loading to complete\r\n if (isLoading || configLoading) return;\r\n\r\n // Without URL routing (single-tenant or hybrid), slug in URL should never happen\r\n // but if it does, we don't need to sync anything\r\n if (!useUrlRouting) return;\r\n\r\n // In global view, don't try to sync URL slug to a tenant.\r\n // The user intentionally chose \"no tenant\" — don't switch back.\r\n if (isGlobalView) return;\r\n\r\n // If URL has a slug that differs from current tenant, switch to it\r\n const urlSlug = slug || extractSlugFromPath(location.pathname);\r\n\r\n // CRITICAL FIX: Only sync from URL if the URL slug actually changed\r\n // This prevents reverting programmatic tenant switches where:\r\n // 1. TenantSelector changes state (currentTenant = newTenant)\r\n // 2. Effect 1 runs and sees URL=oldTenant, state=newTenant\r\n // 3. Without this check, Effect 1 would switch BACK to oldTenant!\r\n const urlSlugChanged = urlSlug !== prevUrlSlugRef.current;\r\n prevUrlSlugRef.current = urlSlug;\r\n\r\n // Skip if URL didn't change - this means state changed, let Effect 2 update URL\r\n if (!urlSlugChanged) {\r\n return;\r\n }\r\n\r\n if (urlSlug && urlSlug !== currentTenant?.slug) {\r\n // Find tenant by slug\r\n const targetTenant = userTenants.find(t => t.slug === urlSlug && t.status === 'Active');\r\n\r\n if (targetTenant) {\r\n // Switch to the tenant from URL\r\n pendingSwitchSlug.current = urlSlug;\r\n switchTenant(targetTenant.id)\r\n .then(() => {\r\n pendingSwitchSlug.current = null;\r\n })\r\n .catch(err => {\r\n pendingSwitchSlug.current = null;\r\n console.error('[TenantRouteWrapper] Failed to switch tenant:', err);\r\n });\r\n } else {\r\n // Invalid slug - redirect to current tenant's URL or home\r\n if (currentTenant) {\r\n const cleanPath = stripSlugFromPath(location.pathname);\r\n navigate(`/t/${currentTenant.slug}${cleanPath}`, { replace: true });\r\n } else {\r\n navigate('/', { replace: true });\r\n }\r\n }\r\n }\r\n }, [\r\n slug,\r\n location.pathname,\r\n currentTenant?.slug,\r\n userTenants,\r\n useUrlRouting,\r\n isLoading,\r\n configLoading,\r\n isGlobalView,\r\n switchTenant,\r\n navigate\r\n ]);\r\n\r\n // Effect 2: Ensure URL matches current state (redirects)\r\n useEffect(() => {\r\n // Wait for loading to complete\r\n if (isLoading || configLoading) return;\r\n\r\n // Prevent navigation loops\r\n if (isNavigating.current) return;\r\n\r\n const currentPath = location.pathname;\r\n const urlSlug = extractSlugFromPath(currentPath);\r\n\r\n // Case 0: Global view with slug in URL — strip the slug\r\n // User entered global view but URL still has /t/{old-slug}/...\r\n if (isGlobalView && urlSlug) {\r\n isNavigating.current = true;\r\n const cleanPath = stripSlugFromPath(currentPath);\r\n navigate(cleanPath, { replace: true });\r\n setTimeout(() => { isNavigating.current = false; }, 100);\r\n return;\r\n }\r\n\r\n // Case 1: No URL routing mode - strip any slug\r\n if (!useUrlRouting && urlSlug) {\r\n isNavigating.current = true;\r\n const cleanPath = stripSlugFromPath(currentPath);\r\n navigate(cleanPath, { replace: true });\r\n setTimeout(() => { isNavigating.current = false; }, 100);\r\n return;\r\n }\r\n\r\n // Case 2: URL routing mode with tenant but no slug - add slug\r\n if (useUrlRouting && currentTenant && !urlSlug && !isGlobalView) {\r\n // Only redirect if we're on a route that should have a slug\r\n // Don't redirect from root \"/\" or public routes\r\n const shouldHaveSlug = currentPath !== '/' &&\r\n !currentPath.startsWith('/login') &&\r\n !currentPath.startsWith('/auth') &&\r\n !currentPath.startsWith('/public');\r\n\r\n if (shouldHaveSlug) {\r\n isNavigating.current = true;\r\n navigate(`/t/${currentTenant.slug}${currentPath}`, { replace: true });\r\n setTimeout(() => { isNavigating.current = false; }, 100);\r\n return;\r\n }\r\n }\r\n\r\n // Case 3: URL slug doesn't match current tenant - update URL\r\n // BUT skip if we're currently switching TO that slug (avoid redirect conflict)\r\n if (useUrlRouting && currentTenant && urlSlug && !isGlobalView &&\r\n urlSlug !== currentTenant.slug && urlSlug !== pendingSwitchSlug.current) {\r\n isNavigating.current = true;\r\n const cleanPath = stripSlugFromPath(currentPath);\r\n navigate(`/t/${currentTenant.slug}${cleanPath}`, { replace: true });\r\n setTimeout(() => { isNavigating.current = false; }, 100);\r\n return;\r\n }\r\n }, [\r\n location.pathname,\r\n currentTenant?.slug,\r\n useUrlRouting,\r\n isLoading,\r\n configLoading,\r\n isGlobalView,\r\n navigate\r\n ]);\r\n\r\n // Render children via Outlet\r\n return <Outlet />;\r\n}\r\n\r\nexport default TenantRouteWrapper;\r\n","import type { ReactElement } from 'react';\nimport { Navigate, useParams } from 'react-router-dom';\r\n\r\ninterface TenantNavigateProps {\r\n /** Absolute path WITHOUT tenant prefix (e.g., \"/administration/configuration/settings\") */\r\n readonly to: string;\r\n readonly replace?: boolean;\r\n}\r\n\r\n/**\r\n * Navigate component that preserves the tenant prefix in multi-tenant routes.\r\n * Use for wildcard catch-all redirects where relative path depth is unknown.\r\n * For non-wildcard routes, prefer standard <Navigate to=\"relative\" /> instead.\r\n */\r\nexport function TenantNavigate({ to, replace = true }: TenantNavigateProps): ReactElement {\r\n const { slug } = useParams<{ slug?: string }>();\r\n const target = slug ? `/t/${slug}${to}` : to;\r\n return <Navigate to={target} replace={replace} />;\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Lock, Settings, User } from 'lucide-react';\r\nimport { Link } from 'react-router-dom';\r\nimport { useTenantUrl } from '@/utils/tenantUrl';\r\nimport { useLicense } from '@/contexts/LicenseContext';\r\nimport { LicenseActivationDialog } from './LicenseActivationDialog';\r\n\r\nexport function LicenseBlockOverlay(): ReactElement {\r\n const { t } = useTranslation('admin');\r\n const { license } = useLicense();\r\n const buildTenantUrl = useTenantUrl();\r\n\r\n const isNoLicense = !license || license.status === 'NoLicense';\r\n\r\n if (isNoLicense) {\r\n return <LicenseActivationDialog open={true} onOpenChange={() => {}} />;\r\n }\r\n\r\n return (\r\n <div className=\"fixed inset-0 z-40 flex items-center justify-center\">\r\n {/* Backdrop */}\r\n <div className=\"absolute inset-0 bg-black/60 backdrop-blur-sm\" />\r\n\r\n {/* Card */}\r\n <div className=\"relative bg-white dark:bg-gray-800 rounded-lg shadow-2xl w-full max-w-md mx-4 overflow-hidden\">\r\n {/* Icon Header */}\r\n <div className=\"flex justify-center pt-8 pb-4\">\r\n <div className=\"p-4 bg-red-100 dark:bg-red-900/30 rounded-full\">\r\n <Lock className=\"w-10 h-10 text-red-600 dark:text-red-400\" />\r\n </div>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"px-6 pb-6 text-center\">\r\n <h2 className=\"text-xl font-bold text-gray-900 dark:text-white mb-2\">\r\n {t('license.guard.title', 'License Required')}\r\n </h2>\r\n <p className=\"text-sm text-gray-600 dark:text-gray-400 mb-6\">\r\n {t('license.guard.description', 'Your license has expired or is invalid. Access to this area is restricted. Please activate a valid license to continue using the platform.')}\r\n </p>\r\n\r\n <div className=\"flex flex-col gap-3\">\r\n <Link\r\n to={buildTenantUrl('/administration/configuration/license')}\r\n className=\"w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors\"\r\n >\r\n <Settings className=\"w-4 h-4\" />\r\n {t('license.guard.manageLicense', 'Manage License')}\r\n </Link>\r\n <Link\r\n to={buildTenantUrl('/myspace/profile')}\r\n className=\"w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors\"\r\n >\r\n <User className=\"w-4 h-4\" />\r\n {t('license.guard.myProfile', 'My Profile')}\r\n </Link>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { Outlet, useLocation } from 'react-router-dom';\r\nimport { useLicense } from '@/contexts/LicenseContext';\r\nimport { LicenseBlockOverlay } from './LicenseBlockOverlay';\r\n\r\n/**\r\n * Paths that are exempt from license enforcement.\r\n * These allow users to access their profile and license configuration\r\n * even when the license is invalid.\r\n */\r\nconst EXEMPT_PREFIXES = ['/myspace', '/administration/configuration'];\r\n\r\n/**\r\n * Strips the tenant prefix (/t/:slug/) from a path to get the logical route.\r\n */\r\nfunction stripTenantPrefix(path: string): string {\r\n const match = /^\\/t\\/[^/]+\\/(.*)/.exec(path);\r\n return match ? `/${match[1]}` : path;\r\n}\r\n\r\n/**\r\n * LicenseGuard - Layout route that enforces license validity.\r\n *\r\n * Inserted between ProtectedRoute and child routes:\r\n * ProtectedRoute → LicenseGuard → Outlet (pages)\r\n *\r\n * Behavior:\r\n * 1. While license is loading → render Outlet (avoid flash)\r\n * 2. Exempt paths (myspace, configuration) → always render Outlet\r\n * 3. Invalid license or read-only mode → show LicenseBlockOverlay over Outlet\r\n * 4. Valid license → render Outlet normally\r\n */\r\nexport function LicenseGuard(): ReactElement {\r\n const { license, isLoading, isReadOnlyMode } = useLicense();\r\n const location = useLocation();\r\n\r\n // While loading, render children to prevent flash\r\n if (isLoading) {\r\n return <Outlet />;\r\n }\r\n\r\n // Strip tenant prefix to get logical path\r\n const logicalPath = stripTenantPrefix(location.pathname);\r\n\r\n // Check if current path is exempt\r\n const isExempt = EXEMPT_PREFIXES.some(prefix =>\r\n logicalPath.startsWith(prefix)\r\n );\r\n\r\n if (isExempt) {\r\n return <Outlet />;\r\n }\r\n\r\n // Block if license is invalid or in read-only mode\r\n if (!license?.isValid || isReadOnlyMode) {\r\n return (\r\n <>\r\n <Outlet />\r\n <LicenseBlockOverlay />\r\n </>\r\n );\r\n }\r\n\r\n // License is valid — render normally\r\n return <Outlet />;\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { Outlet } from 'react-router-dom';\r\nimport { useLicense } from '@/contexts/LicenseContext';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Lock, Sparkles } from 'lucide-react';\r\n\r\ninterface Props {\r\n /** Feature code to check (e.g., 'ai', 'workflows', 'support') */\r\n readonly feature: string;\r\n}\r\n\r\n/**\r\n * FeatureRouteGuard - Layout route that enforces license feature availability.\r\n *\r\n * Wraps a group of routes requiring a specific license feature:\r\n * <Route element={<FeatureRouteGuard feature=\"ai\" />}>\r\n * <Route path=\"ai/dashboard\" element={<AiDashboardPage />} />\r\n * </Route>\r\n *\r\n * Behavior:\r\n * 1. While loading → render Outlet (avoid flash)\r\n * 2. Wildcard '*' in enabledFeatures → always render Outlet\r\n * 3. Feature enabled → render Outlet\r\n * 4. Feature disabled → show locked placeholder\r\n */\r\nexport function FeatureRouteGuard({ feature }: Props): ReactElement {\r\n const { t } = useTranslation('admin');\r\n const { isFeatureEnabled, license, isLoading } = useLicense();\r\n\r\n // While loading, render children to prevent flash\r\n if (isLoading) {\r\n return <Outlet />;\r\n }\r\n\r\n // Feature is enabled\r\n if (isFeatureEnabled(feature)) {\r\n return <Outlet />;\r\n }\r\n\r\n // Feature not available — show locked placeholder\r\n return (\r\n <div className=\"flex items-center justify-center min-h-[60vh] p-6\">\r\n <div className=\"flex flex-col items-center justify-center p-8 bg-gray-50 dark:bg-gray-800/50 rounded-xl border-2 border-dashed border-gray-200 dark:border-gray-700 max-w-md\">\r\n <div className=\"p-4 bg-gray-100 dark:bg-gray-700 rounded-full mb-4\">\r\n <Lock className=\"w-8 h-8 text-gray-400 dark:text-gray-500\" />\r\n </div>\r\n\r\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-white mb-2\">\r\n {t('license.featureNotAvailable', 'Feature Not Available')}\r\n </h3>\r\n\r\n <p className=\"text-sm text-gray-500 dark:text-gray-400 text-center max-w-sm mb-4\">\r\n {t('license.upgradeToAccess', 'Upgrade your license to access the {{feature}} feature.', { feature })}\r\n </p>\r\n\r\n <div className=\"flex items-center gap-2 text-xs text-gray-400 dark:text-gray-500\">\r\n <Sparkles className=\"w-4 h-4\" />\r\n <span>\r\n {t('license.currentEdition', 'Current: {{edition}}', { edition: license?.edition || 'Unknown' })}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { useInRouterContext } from 'react-router-dom';\r\nimport { usePageTracking } from '@/hooks/usePageTracking';\r\n\r\n/**\r\n * Internal component that actually does the tracking\r\n * Only rendered when inside a Router context\r\n */\r\nfunction PageTrackerInner() {\r\n usePageTracking();\r\n return null;\r\n}\r\n\r\n/**\r\n * Component that tracks page navigation automatically\r\n * Place it inside your router to enable tracking.\r\n *\r\n * Note: This component safely handles being rendered outside a Router context\r\n * (e.g., when SmartStackProvider wraps RouterProvider instead of being inside it).\r\n * In that case, it simply doesn't render anything.\r\n */\r\nexport function PageTracker(): ReactElement | null {\r\n // Check if we're inside a Router context using the official react-router API\r\n const inRouterContext = useInRouterContext();\r\n\r\n // If not inside a Router, don't render anything (and don't call useLocation)\r\n if (!inRouterContext) {\r\n return null;\r\n }\r\n\r\n return <PageTrackerInner />;\r\n}\r\n","import { api } from './apiClient';\r\n\r\n// DTOs\r\nexport interface TopTenantDto {\r\n tenantId: string;\r\n tenantName: string;\r\n tenantSlug: string;\r\n tenantType: string;\r\n accessCount: number;\r\n totalDurationMinutes: number;\r\n uniqueUsers: number;\r\n}\r\n\r\nexport interface DailyTenantTrendDto {\r\n date: string;\r\n accessCount: number;\r\n uniqueUsers: number;\r\n totalDurationMinutes: number;\r\n}\r\n\r\nexport interface TenantActivityItemDto {\r\n userId: string;\r\n userName: string;\r\n tenantName: string;\r\n tenantSlug: string;\r\n accessedAt: string;\r\n durationSeconds?: number;\r\n}\r\n\r\nexport interface TenantDashboardStatsDto {\r\n totalTenants: number;\r\n activeTenants: number;\r\n pendingTenants: number;\r\n suspendedTenants: number;\r\n totalMembers: number;\r\n activeUsersToday: number;\r\n avgSessionDuration: number;\r\n growthRate: number;\r\n pendingAccessRequests: number;\r\n approvedAccessRequests: number;\r\n deniedAccessRequests: number;\r\n topTenants: TopTenantDto[];\r\n dailyTrend: DailyTenantTrendDto[];\r\n recentActivity: TenantActivityItemDto[];\r\n}\r\n\r\nexport interface TenantTrendDayDto {\r\n date: string;\r\n tenantCounts: Record<string, number>;\r\n tenantDurations: Record<string, number>;\r\n}\r\n\r\nexport interface TenantSeriesDto {\r\n name: string;\r\n totalAccess: number;\r\n totalDurationMinutes: number;\r\n color: string;\r\n}\r\n\r\nexport interface TenantTrendsDto {\r\n period: number;\r\n dailyData: TenantTrendDayDto[];\r\n tenants: TenantSeriesDto[];\r\n}\r\n\r\nexport interface TenantTrackingResponseDto {\r\n accessId: string;\r\n}\r\n\r\nconst BASE_URL = '/api/administration/tenants/tenant-analytics';\r\n\r\nexport const tenantAnalyticsApi = {\r\n getDashboardStats: async (period: number = 30): Promise<TenantDashboardStatsDto> => {\r\n return await api.get<TenantDashboardStatsDto>(`${BASE_URL}/dashboard`, {\r\n params: { period }\r\n });\r\n },\r\n\r\n getTopTenants: async (\r\n limit: number = 10,\r\n period: number = 30,\r\n sortBy: 'access' | 'duration' | 'users' = 'access'\r\n ): Promise<TopTenantDto[]> => {\r\n return await api.get<TopTenantDto[]>(`${BASE_URL}/top-tenants`, {\r\n params: { limit, period, sortBy }\r\n });\r\n },\r\n\r\n getTrends: async (period: number = 30): Promise<TenantTrendsDto> => {\r\n return await api.get<TenantTrendsDto>(`${BASE_URL}/trends`, {\r\n params: { period }\r\n });\r\n },\r\n\r\n trackStart: async (tenantId: string): Promise<TenantTrackingResponseDto> => {\r\n return await api.post<TenantTrackingResponseDto>(`${BASE_URL}/track/start`, {\r\n tenantId\r\n });\r\n },\r\n\r\n trackEnd: async (accessId: string): Promise<void> => {\r\n await api.post(`${BASE_URL}/track/end`, { accessId });\r\n }\r\n};\r\n","import { useEffect, useRef } from 'react';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { tenantAnalyticsApi } from '@/services/api/tenantAnalyticsApi';\r\n\r\nconst TRACKING_API = '/api/administration/tenants/tenant-analytics';\r\n\r\n/**\r\n * Tracks tenant access sessions.\r\n * Calls trackStart when user enters a tenant context,\r\n * and trackEnd when they leave (switch tenant, logout, or unmount).\r\n */\r\nexport function useTenantTracking(): void {\r\n const { currentTenant } = useTenant();\r\n const { isAuthenticated } = useAuth();\r\n const currentAccessIdRef = useRef<string | null>(null);\r\n const currentTenantIdRef = useRef<string | null>(null);\r\n const isTrackingRef = useRef(false);\r\n\r\n useEffect(() => {\r\n if (!isAuthenticated) return;\r\n\r\n // Skip tracking on auth pages (onboarding, login, register, etc.)\r\n const path = window.location.pathname;\r\n if (path.startsWith('/auth/') || path.startsWith('/login')) return;\r\n\r\n const tenantId = currentTenant?.id ?? null;\r\n\r\n // No change\r\n if (tenantId === currentTenantIdRef.current) return;\r\n\r\n // Prevent duplicate concurrent tracking calls\r\n if (isTrackingRef.current) return;\r\n isTrackingRef.current = true;\r\n\r\n const updateTracking = async () => {\r\n // End previous session if exists\r\n if (currentAccessIdRef.current) {\r\n try {\r\n await tenantAnalyticsApi.trackEnd(currentAccessIdRef.current);\r\n } catch {\r\n // Best-effort: silently ignore errors\r\n }\r\n currentAccessIdRef.current = null;\r\n }\r\n\r\n currentTenantIdRef.current = tenantId;\r\n\r\n // Start new session if entering a tenant\r\n if (tenantId) {\r\n try {\r\n const response = await tenantAnalyticsApi.trackStart(tenantId);\r\n currentAccessIdRef.current = response.accessId;\r\n } catch {\r\n // Best-effort: silently ignore errors (e.g. 401, 403)\r\n currentAccessIdRef.current = null;\r\n }\r\n }\r\n\r\n isTrackingRef.current = false;\r\n };\r\n\r\n updateTracking();\r\n\r\n // Cleanup: end tracking on unmount or before next effect\r\n return () => {\r\n if (currentAccessIdRef.current) {\r\n const token = localStorage.getItem('token');\r\n if (!token) return;\r\n\r\n // Use fetch with keepalive for reliable tracking on page unload\r\n fetch(`${TRACKING_API}/track/end`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Authorization': `Bearer ${token}`\r\n },\r\n body: JSON.stringify({ accessId: currentAccessIdRef.current }),\r\n keepalive: true\r\n }).catch(() => {\r\n // Silently ignore errors on unmount\r\n });\r\n\r\n currentAccessIdRef.current = null;\r\n currentTenantIdRef.current = null;\r\n }\r\n };\r\n }, [currentTenant?.id, isAuthenticated]);\r\n}\r\n","import type { ReactElement } from 'react';\nimport { useTenantTracking } from '@/hooks/useTenantTracking';\r\n\r\n/**\r\n * Component that tracks tenant access sessions automatically.\r\n * Place it inside the TenantProvider to enable tracking.\r\n */\r\nexport function TenantTracker(): ReactElement | null {\r\n useTenantTracking();\r\n return null;\r\n}\r\n","import { lazy } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { Route, Navigate } from 'react-router-dom';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Layouts\r\n// ---------------------------------------------------------------------------\r\nconst DocsLayout = lazy(() => import('@/layouts/DocsLayout').then(m => ({ default: m.DocsLayout })));\r\n\r\n// ---------------------------------------------------------------------------\r\n// System Documentation pages\r\n// ---------------------------------------------------------------------------\r\nconst DocsIndexPage = lazy(() => import('@/pages/system/docs/DocsIndexPage').then(m => ({ default: m.DocsIndexPage })));\r\nconst SmartStackPresentationPage = lazy(() => import('@/pages/system/docs/SmartStackPresentationPage').then(m => ({ default: m.SmartStackPresentationPage })));\r\nconst DeveloperIndexPage = lazy(() => import('@/pages/system/docs/developer/DeveloperIndexPage').then(m => ({ default: m.DeveloperIndexPage })));\r\nconst InfrastructurePage = lazy(() => import('@/pages/system/docs/developer/InfrastructurePage').then(m => ({ default: m.InfrastructurePage })));\r\nconst InstallationPage = lazy(() => import('@/pages/system/docs/developer/InstallationPage').then(m => ({ default: m.InstallationPage })));\r\nconst DatabasePage = lazy(() => import('@/pages/system/docs/developer/DatabasePage').then(m => ({ default: m.DatabasePage })));\r\nconst DualDbContextPage = lazy(() => import('@/pages/system/docs/developer/DualDbContextPage').then(m => ({ default: m.DualDbContextPage })));\r\nconst CrossSchemaFkPage = lazy(() => import('@/pages/system/docs/developer/CrossSchemaFkPage').then(m => ({ default: m.CrossSchemaFkPage })));\r\nconst TenantIndexPage = lazy(() => import('@/pages/system/docs/developer/tenants').then(m => ({ default: m.TenantIndexPage })));\r\nconst MultiTenantModePage = lazy(() => import('@/pages/system/docs/developer/tenants').then(m => ({ default: m.MultiTenantModePage })));\r\nconst TenantB2CPage = lazy(() => import('@/pages/system/docs/developer/tenants').then(m => ({ default: m.TenantB2CPage })));\r\nconst TeamsIntegrationPage = lazy(() => import('@/pages/system/docs/developer/TeamsIntegrationPage').then(m => ({ default: m.TeamsIntegrationPage })));\r\nconst SecurityTestingPage = lazy(() => import('@/pages/system/docs/developer/testing').then(m => ({ default: m.SecurityTestingPage })));\r\nconst UserIndexPage = lazy(() => import('@/pages/system/docs/user/UserIndexPage').then(m => ({ default: m.UserIndexPage })));\r\nconst ArchitecturePage = lazy(() => import('@/pages/system/docs/user/ArchitecturePage').then(m => ({ default: m.ArchitecturePage })));\r\nconst ModulesPage = lazy(() => import('@/pages/system/docs/user/ModulesPage').then(m => ({ default: m.ModulesPage })));\r\nconst SlaDocPage = lazy(() => import('@/pages/system/docs/user/SlaDocPage').then(m => ({ default: m.SlaDocPage })));\r\nconst SupportIndexPage = lazy(() => import('@/pages/system/docs/user/SupportIndexPage').then(m => ({ default: m.SupportIndexPage })));\r\nconst SupportDashboardDocPage = lazy(() => import('@/pages/system/docs/user/SupportDashboardDocPage').then(m => ({ default: m.SupportDashboardDocPage })));\r\nconst SupportTicketsDocPage = lazy(() => import('@/pages/system/docs/user/SupportTicketsDocPage').then(m => ({ default: m.SupportTicketsDocPage })));\r\nconst SupportMyTicketsDocPage = lazy(() => import('@/pages/system/docs/user/SupportMyTicketsDocPage').then(m => ({ default: m.SupportMyTicketsDocPage })));\r\nconst SupportTemplatesDocPage = lazy(() => import('@/pages/system/docs/user/SupportTemplatesDocPage').then(m => ({ default: m.SupportTemplatesDocPage })));\r\nconst SupportEscalationDocPage = lazy(() => import('@/pages/system/docs/user/SupportEscalationDocPage').then(m => ({ default: m.SupportEscalationDocPage })));\r\nconst AiDocPage = lazy(() => import('@/pages/system/docs/user/AiDocPage').then(m => ({ default: m.AiDocPage })));\r\nconst SettingsDocPage = lazy(() => import('@/pages/system/docs/user/SettingsDocPage').then(m => ({ default: m.SettingsDocPage })));\r\nconst ThemeDocPage = lazy(() => import('@/pages/system/docs/user/ThemeDocPage').then(m => ({ default: m.ThemeDocPage })));\r\nconst BaIndexPage = lazy(() => import('@/pages/system/docs/ba/BaIndexPage').then(m => ({ default: m.BaIndexPage })));\r\nconst BaViewerPage = lazy(() => import('@/pages/system/docs/ba/BaViewerPage').then(m => ({ default: m.BaViewerPage })));\r\n\r\n// ---------------------------------------------------------------------------\r\n// Business Documentation pages\r\n// ---------------------------------------------------------------------------\r\nconst UsersDocPage = lazy(() => import('@/pages/docs/business/platform/administration/users'));\r\nconst UsersDashboardDocPage = lazy(() => import('@/pages/docs/business/platform/administration/users/dashboard'));\r\nconst UsersReferencesDocPage = lazy(() => import('@/pages/docs/business/platform/administration/users/references'));\r\nconst UsersGroupsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/users/groups'));\r\nconst PermissionsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/permissions'));\r\nconst AssignmentsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/permissions/assignments'));\r\nconst TenantsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/tenants'));\r\nconst TenantTemplatesDocPage = lazy(() => import('@/pages/docs/business/platform/administration/tenants/template'));\r\nconst BusinessTenantsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/tenants/business'));\r\nconst PersonalTenantsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/tenants/personal'));\r\nconst OtherTenantsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/tenants/other'));\r\nconst TenantApplicationsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/tenants/applications'));\r\nconst AccessRequestsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/tenants/access-requests'));\r\nconst OrganisationsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/tenants/organisations'));\r\nconst AiAdminDocPage = lazy(() => import('@/pages/docs/business/platform/administration/ai'));\r\nconst WorkflowsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/workflows'));\r\nconst EntraDocPage = lazy(() => import('@/pages/docs/business/platform/administration/entra'));\r\nconst ApplicationsDocPage = lazy(() => import('@/pages/docs/business/platform/administration/applications'));\r\nconst ConfigurationDocPage = lazy(() => import('@/pages/docs/business/platform/administration/configuration'));\r\nconst LicenseDocPage = lazy(() => import('@/pages/docs/business/platform/administration/configuration/license'));\r\nconst PlatformSupportTicketsDocPage = lazy(() => import('@/pages/docs/business/platform/support/tickets'));\r\n\r\n/**\r\n * DocRoutes — Renders all documentation routes.\r\n * Includes system documentation, business documentation, and legacy redirects.\r\n * Always accessible, not from DB navigation.\r\n */\r\nexport function DocRoutes(): ReactElement {\r\n return (\r\n <>\r\n <Route path=\"/system/docs\" element={<DocsLayout />}>\r\n <Route index element={<DocsIndexPage />} />\r\n {/* Developer docs */}\r\n <Route path=\"developer\" element={<DeveloperIndexPage />} />\r\n <Route path=\"developer/infrastructure\" element={<InfrastructurePage />} />\r\n <Route path=\"developer/installation\" element={<InstallationPage />} />\r\n <Route path=\"developer/database\" element={<DatabasePage />} />\r\n <Route path=\"developer/dual-dbcontext\" element={<DualDbContextPage />} />\r\n <Route path=\"developer/cross-schema-fk\" element={<CrossSchemaFkPage />} />\r\n <Route path=\"developer/tenants\" element={<TenantIndexPage />} />\r\n <Route path=\"developer/tenants/multi-tenant-mode\" element={<MultiTenantModePage />} />\r\n <Route path=\"developer/tenants/b2c\" element={<TenantB2CPage />} />\r\n <Route path=\"developer/multi-tenant-mode\" element={<Navigate to=\"/system/docs/developer/tenants/multi-tenant-mode\" replace />} />\r\n <Route path=\"developer/teams-integration\" element={<TeamsIntegrationPage />} />\r\n <Route path=\"developer/testing/security\" element={<SecurityTestingPage />} />\r\n {/* User docs */}\r\n <Route path=\"user\" element={<UserIndexPage />} />\r\n <Route path=\"user/architecture\" element={<ArchitecturePage />} />\r\n <Route path=\"user/modules\" element={<ModulesPage />} />\r\n <Route path=\"user/support\" element={<SupportIndexPage />} />\r\n <Route path=\"user/support-dashboard\" element={<SupportDashboardDocPage />} />\r\n <Route path=\"user/support-tickets\" element={<SupportTicketsDocPage />} />\r\n <Route path=\"user/support-my-tickets\" element={<SupportMyTicketsDocPage />} />\r\n <Route path=\"user/support-sla\" element={<SlaDocPage />} />\r\n <Route path=\"user/support-templates\" element={<SupportTemplatesDocPage />} />\r\n <Route path=\"user/support-escalation\" element={<SupportEscalationDocPage />} />\r\n <Route path=\"user/ai\" element={<AiDocPage />} />\r\n <Route path=\"user/settings\" element={<SettingsDocPage />} />\r\n <Route path=\"user/theme\" element={<ThemeDocPage />} />\r\n {/* Business Analysis viewer */}\r\n <Route path=\"ba\" element={<BaIndexPage />} />\r\n <Route path=\"ba/:appCode\" element={<BaViewerPage />} />\r\n <Route path=\"ba/:appCode/:moduleCode\" element={<BaViewerPage />} />\r\n </Route>\r\n\r\n {/* Business Documentation */}\r\n <Route path=\"/docs/business\" element={<DocsLayout />}>\r\n <Route path=\"administration/users\" element={<UsersDocPage />} />\r\n <Route path=\"administration/users/dashboard\" element={<UsersDashboardDocPage />} />\r\n <Route path=\"administration/users/references\" element={<UsersReferencesDocPage />} />\r\n <Route path=\"administration/users/groups\" element={<UsersGroupsDocPage />} />\r\n <Route path=\"administration/permissions\" element={<PermissionsDocPage />} />\r\n <Route path=\"administration/permissions/assignments\" element={<AssignmentsDocPage />} />\r\n <Route path=\"administration/tenants\" element={<TenantsDocPage />} />\r\n <Route path=\"administration/tenants/template\" element={<TenantTemplatesDocPage />} />\r\n <Route path=\"administration/tenants/business\" element={<BusinessTenantsDocPage />} />\r\n <Route path=\"administration/tenants/personal\" element={<PersonalTenantsDocPage />} />\r\n <Route path=\"administration/tenants/other\" element={<OtherTenantsDocPage />} />\r\n <Route path=\"administration/tenants/applications\" element={<TenantApplicationsDocPage />} />\r\n <Route path=\"administration/tenants/access-requests\" element={<AccessRequestsDocPage />} />\r\n <Route path=\"administration/tenants/organisations\" element={<OrganisationsDocPage />} />\r\n <Route path=\"administration/ai\" element={<AiAdminDocPage />} />\r\n <Route path=\"administration/workflows\" element={<WorkflowsDocPage />} />\r\n <Route path=\"administration/entra\" element={<EntraDocPage />} />\r\n <Route path=\"administration/applications\" element={<ApplicationsDocPage />} />\r\n <Route path=\"administration/configuration\" element={<ConfigurationDocPage />} />\r\n <Route path=\"administration/configuration/license\" element={<LicenseDocPage />} />\r\n <Route path=\"platform/support/tickets\" element={<PlatformSupportTicketsDocPage />} />\r\n </Route>\r\n\r\n {/* Full-screen presentation — outside DocsLayout */}\r\n <Route path=\"/system/docs/presentation\" element={<SmartStackPresentationPage />} />\r\n\r\n {/* Legacy /docs redirect (AFTER /docs/business to avoid catch-all conflict) */}\r\n <Route path=\"/docs/*\" element={<Navigate to=\"/system/docs\" replace />} />\r\n <Route path=\"/docs\" element={<Navigate to=\"/system/docs\" replace />} />\r\n </>\r\n );\r\n}\r\n","/**\r\n * Sections that use Outlet-based tab navigation.\r\n *\r\n * The section component renders an <Outlet /> and tab children\r\n * are resolved from PageRegistry by key: `{sectionKey}.{tabCode}`.\r\n *\r\n * When adding new tab-based sections:\r\n * 1. Add entry here with defaultTab and tabs array\r\n * 2. Register each tab page in componentRegistry.generated.ts\r\n * 3. Ensure the section page renders an <Outlet /> for nested routes\r\n */\r\nexport const OUTLET_SECTIONS: Record<string, { defaultTab: string; tabs: string[] }> = {\r\n 'administration.configuration.settings': {\r\n defaultTab: 'general',\r\n tabs: ['general', 'file-upload', 'legal-file'],\r\n },\r\n 'administration.configuration.appsettings': {\r\n defaultTab: 'infrastructure',\r\n tabs: ['infrastructure', 'authentication', 'logging', 'email', 'entra'],\r\n },\r\n};\r\n","import type { ReactElement } from 'react';\nimport {\r\n Users,\r\n Bell,\r\n HeadphonesIcon,\r\n Shield,\r\n Key,\r\n Layers,\r\n ArrowRight,\r\n Zap,\r\n Lock,\r\n Rocket,\r\n Check,\r\n} from 'lucide-react';\r\n\r\nconst features = [\r\n {\r\n icon: Users,\r\n title: 'Gestion des Utilisateurs',\r\n description:\r\n 'Créez, modifiez et gérez vos utilisateurs avec une interface intuitive. Profils complets, avatars et informations personnalisables.',\r\n color: 'from-blue-500 to-cyan-500',\r\n },\r\n {\r\n icon: Bell,\r\n title: 'Gestion des Notifications',\r\n description:\r\n 'Système de notifications en temps réel. Email, push, in-app. Templates personnalisables et historique complet.',\r\n color: 'from-amber-500 to-orange-500',\r\n },\r\n {\r\n icon: HeadphonesIcon,\r\n title: 'Gestion du Support',\r\n description:\r\n 'Système de tickets intégré pour le support client. Suivi, priorités, assignation et statistiques.',\r\n color: 'from-emerald-500 to-teal-500',\r\n },\r\n {\r\n icon: Shield,\r\n title: 'Panneau Administrateur',\r\n description:\r\n 'Dashboard complet pour administrer votre plateforme. Métriques, logs, configuration et monitoring.',\r\n color: 'from-purple-500 to-pink-500',\r\n },\r\n {\r\n icon: Key,\r\n title: 'Gestion des Rôles',\r\n description:\r\n 'RBAC avancé avec permissions granulaires. Hiérarchie de rôles, wildcards et héritage des droits.',\r\n color: 'from-red-500 to-rose-500',\r\n },\r\n {\r\n icon: Layers,\r\n title: 'Architecture Modulaire',\r\n description:\r\n 'Structure en 5 niveaux : Contexte, Application, Module, Section, Resource. Extensible et maintenable.',\r\n color: 'from-indigo-500 to-violet-500',\r\n },\r\n];\r\n\r\nconst benefits = [\r\n { icon: Zap, text: 'Démarrage rapide' },\r\n { icon: Lock, text: 'Sécurité intégrée' },\r\n { icon: Rocket, text: 'Performance optimale' },\r\n];\r\n\r\nconst techStack = [\r\n '.NET 10',\r\n 'React 19',\r\n 'TypeScript',\r\n 'EF Core',\r\n 'SQL Server',\r\n 'Tailwind CSS',\r\n];\r\n\r\nexport function HomePage(): ReactElement {\r\n return (\r\n <div className=\"flex flex-col\">\r\n {/* Hero Section */}\r\n <section className=\"relative pt-32 pb-20 md:pt-40 md:pb-32 overflow-hidden\">\r\n {/* Background decoration */}\r\n <div className=\"absolute inset-0 overflow-hidden pointer-events-none\">\r\n <div className=\"absolute -top-40 -right-40 w-80 h-80 rounded-full bg-primary-500/10 blur-3xl\" />\r\n <div className=\"absolute -bottom-40 -left-40 w-80 h-80 rounded-full bg-accent-500/10 blur-3xl\" />\r\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] rounded-full bg-gradient-to-r from-primary-500/5 to-accent-500/5 blur-3xl\" />\r\n </div>\r\n\r\n <div className=\"relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\r\n <div className=\"text-center\">\r\n {/* Badge */}\r\n <div className=\"inline-flex items-center gap-2 px-4 py-2 rounded-full bg-primary-500/10 border border-primary-500/20 mb-8\">\r\n <Zap className=\"w-4 h-4 text-primary-500\" />\r\n <span className=\"text-sm font-medium text-primary-500\">\r\n Version 1.0 disponible\r\n </span>\r\n </div>\r\n\r\n {/* Title */}\r\n <h1 className=\"text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold tracking-tight mb-6\">\r\n Le socle moderne pour\r\n <br />\r\n <span className=\"gradient-text\">vos applications</span>\r\n </h1>\r\n\r\n {/* Subtitle */}\r\n <p className=\"max-w-2xl mx-auto text-lg md:text-xl text-[var(--text-secondary)] mb-10\">\r\n SmartStack est une plateforme complète pour créer des applications\r\n d'entreprise. Authentification, rôles, notifications et support\r\n intégrés.\r\n </p>\r\n\r\n {/* CTA Buttons */}\r\n <div className=\"flex flex-col sm:flex-row items-center justify-center gap-4 mb-16\">\r\n <a\r\n href=\"/register\"\r\n className=\"w-full sm:w-auto inline-flex items-center justify-center gap-2 px-8 py-4 rounded-xl bg-primary-600 hover:bg-primary-700 text-white font-semibold transition-all shadow-lg shadow-primary-500/25 hover:shadow-primary-500/40\"\r\n >\r\n Commencer gratuitement\r\n <ArrowRight className=\"w-5 h-5\" />\r\n </a>\r\n <a\r\n href=\"#features\"\r\n className=\"w-full sm:w-auto inline-flex items-center justify-center gap-2 px-8 py-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-secondary)] border border-[var(--border-color)] font-semibold transition-all\"\r\n >\r\n Découvrir les fonctionnalités\r\n </a>\r\n </div>\r\n\r\n {/* Benefits */}\r\n <div className=\"flex flex-wrap items-center justify-center gap-6\">\r\n {benefits.map((benefit) => (\r\n <div key={benefit.text} className=\"flex items-center gap-2 text-[var(--text-secondary)]\">\r\n <benefit.icon className=\"w-5 h-5 text-primary-500\" />\r\n <span>{benefit.text}</span>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </section>\r\n\r\n {/* Tech Stack Banner */}\r\n <section className=\"py-8 bg-[var(--bg-secondary)] border-y border-[var(--border-color)]\">\r\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\r\n <div className=\"flex flex-wrap items-center justify-center gap-8 md:gap-12\">\r\n <span className=\"text-[var(--text-tertiary)] text-sm uppercase tracking-wider\">\r\n Construit avec\r\n </span>\r\n {techStack.map((tech) => (\r\n <span\r\n key={tech}\r\n className=\"text-[var(--text-secondary)] font-medium\"\r\n >\r\n {tech}\r\n </span>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n\r\n {/* Features Section */}\r\n <section id=\"features\" className=\"py-20 md:py-32\">\r\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\r\n {/* Section Header */}\r\n <div className=\"text-center mb-16\">\r\n <h2 className=\"text-3xl md:text-4xl font-bold mb-4\">\r\n Tout ce dont vous avez besoin\r\n </h2>\r\n <p className=\"max-w-2xl mx-auto text-[var(--text-secondary)]\">\r\n SmartStack inclut toutes les fonctionnalités essentielles pour\r\n démarrer votre projet d'entreprise.\r\n </p>\r\n </div>\r\n\r\n {/* Features Grid */}\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\r\n {features.map((feature) => (\r\n <div\r\n key={feature.title}\r\n className=\"group p-6 rounded-2xl bg-[var(--bg-card)] border border-[var(--border-color)] hover-card\"\r\n >\r\n <div\r\n className={`w-12 h-12 rounded-xl bg-gradient-to-br ${feature.color} flex items-center justify-center mb-4`}\r\n >\r\n <feature.icon className=\"w-6 h-6 text-white\" />\r\n </div>\r\n <h3 className=\"text-xl font-semibold mb-2\">{feature.title}</h3>\r\n <p className=\"text-[var(--text-secondary)]\">\r\n {feature.description}\r\n </p>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n\r\n {/* Modules Section */}\r\n <section id=\"modules\" className=\"py-20 md:py-32 bg-[var(--bg-secondary)]\">\r\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\r\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-12 items-center\">\r\n {/* Content */}\r\n <div>\r\n <h2 className=\"text-3xl md:text-4xl font-bold mb-6\">\r\n Architecture modulaire\r\n <br />\r\n <span className=\"gradient-text\">prête pour la production</span>\r\n </h2>\r\n <p className=\"text-[var(--text-secondary)] mb-8\">\r\n SmartStack utilise une architecture en 5 niveaux qui permet une\r\n organisation claire et une extensibilité maximale. Chaque niveau\r\n correspond à un scope de permissions.\r\n </p>\r\n\r\n <ul className=\"space-y-4 mb-8\">\r\n {[\r\n 'Contexte - Environnements séparés (Platform, Business, Personal)',\r\n 'Application - Apps au sein d\\'un contexte (Admin, MySpace)',\r\n 'Module - Fonctionnalités (Users, Roles, Profile)',\r\n 'Section - Sous-divisions des modules',\r\n 'Resource - Éléments individuels',\r\n ].map((item) => (\r\n <li key={item} className=\"flex items-start gap-3\">\r\n <div className=\"flex-shrink-0 w-6 h-6 rounded-full bg-primary-500/10 flex items-center justify-center mt-0.5\">\r\n <Check className=\"w-4 h-4 text-primary-500\" />\r\n </div>\r\n <span className=\"text-[var(--text-secondary)]\">{item}</span>\r\n </li>\r\n ))}\r\n </ul>\r\n\r\n </div>\r\n\r\n {/* Visual */}\r\n <div className=\"relative\">\r\n <div className=\"absolute inset-0 bg-gradient-to-r from-primary-500/20 to-accent-500/20 rounded-3xl blur-2xl\" />\r\n <div className=\"relative p-8 rounded-3xl bg-[var(--bg-card)] border border-[var(--border-color)]\">\r\n <div className=\"space-y-4\">\r\n {/* Context Level */}\r\n <div className=\"p-4 rounded-xl bg-gradient-to-r from-primary-500/10 to-primary-500/5 border border-primary-500/20\">\r\n <div className=\"flex items-center gap-3 mb-2\">\r\n <div className=\"w-3 h-3 rounded-full bg-primary-500\" />\r\n <span className=\"font-semibold text-primary-500\">\r\n Contexte: Platform\r\n </span>\r\n </div>\r\n\r\n {/* Application Level */}\r\n <div className=\"ml-4 mt-3 p-3 rounded-lg bg-accent-500/10 border border-accent-500/20\">\r\n <div className=\"flex items-center gap-3 mb-2\">\r\n <div className=\"w-2.5 h-2.5 rounded-full bg-accent-500\" />\r\n <span className=\"font-medium text-accent-500\">\r\n Application: Admin\r\n </span>\r\n </div>\r\n\r\n {/* Module Level */}\r\n <div className=\"ml-4 mt-2 space-y-2\">\r\n <div className=\"p-2 rounded bg-[var(--bg-tertiary)] border border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Users className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n <span className=\"text-sm\">Module: Users</span>\r\n </div>\r\n </div>\r\n <div className=\"p-2 rounded bg-[var(--bg-tertiary)] border border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Key className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n <span className=\"text-sm\">Module: Roles</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </section>\r\n\r\n {/* CTA Section */}\r\n <section id=\"contact\" className=\"py-20 md:py-32\">\r\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\r\n <div className=\"relative overflow-hidden rounded-3xl animated-gradient p-px\">\r\n <div className=\"relative rounded-3xl bg-[var(--bg-primary)] p-8 md:p-16 text-center\">\r\n <h2 className=\"text-3xl md:text-4xl font-bold mb-4\">\r\n Prêt à commencer ?\r\n </h2>\r\n <p className=\"max-w-2xl mx-auto text-[var(--text-secondary)] mb-8\">\r\n Lancez votre projet d'entreprise avec SmartStack. Tout est déjà\r\n configuré pour vous permettre de vous concentrer sur votre métier.\r\n </p>\r\n <div className=\"flex flex-col sm:flex-row items-center justify-center gap-4\">\r\n <a\r\n href=\"/register\"\r\n className=\"w-full sm:w-auto inline-flex items-center justify-center gap-2 px-8 py-4 rounded-xl bg-primary-600 hover:bg-primary-700 text-white font-semibold transition-all shadow-lg shadow-primary-500/25 hover:shadow-primary-500/40\"\r\n >\r\n Créer un compte\r\n <ArrowRight className=\"w-5 h-5\" />\r\n </a>\r\n <a\r\n href=\"/contact\"\r\n className=\"w-full sm:w-auto inline-flex items-center justify-center gap-2 px-8 py-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-secondary)] border border-[var(--border-color)] font-semibold transition-all\"\r\n >\r\n Nous contacter\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </section>\r\n </div>\r\n );\r\n}\r\n","export interface ParsedUserAgent {\r\n browser: string;\r\n browserVersion: string;\r\n os: string;\r\n osVersion: string;\r\n device: string;\r\n formatted: string;\r\n}\r\n\r\n/**\r\n * Parses a User-Agent string into readable components\r\n * @param userAgent The User-Agent string to parse\r\n * @returns Parsed components with browser, OS, device and formatted string\r\n */\r\nconst extractVersion = (userAgent: string, pattern: RegExp): string => {\r\n const match = pattern.exec(userAgent);\r\n return match ? match[1] : '';\r\n};\r\n\r\nconst browserDetectors = [\r\n { detect: (ua: string) => ua.includes('edg/') || ua.includes('edge/'), name: 'Edge', pattern: /Edg(?:e)?\\/(\\d+)/i },\r\n { detect: (ua: string) => ua.includes('opr/') || ua.includes('opera'), name: 'Opera', pattern: /(?:OPR|Opera)\\/(\\d+)/i },\r\n { detect: (ua: string) => ua.includes('chrome') && !ua.includes('edg'), name: 'Chrome', pattern: /Chrome\\/(\\d+)/i },\r\n { detect: (ua: string) => ua.includes('safari') && !ua.includes('chrome'), name: 'Safari', pattern: /Version\\/(\\d+)/i },\r\n { detect: (ua: string) => ua.includes('firefox'), name: 'Firefox', pattern: /Firefox\\/(\\d+)/i },\r\n { detect: (ua: string) => ua.includes('msie') || ua.includes('trident'), name: 'Internet Explorer', pattern: /(?:MSIE |rv:)(\\d+)/i },\r\n];\r\n\r\nconst detectBrowser = (ua: string, userAgent: string): { browser: string; version: string } => {\r\n const detector = browserDetectors.find(b => b.detect(ua));\r\n if (detector) {\r\n return { browser: detector.name, version: extractVersion(userAgent, detector.pattern) };\r\n }\r\n return { browser: 'Unknown', version: '' };\r\n};\r\n\r\nconst windowsVersionMap: Record<string, string> = {\r\n 'windows nt 10': '10/11',\r\n 'windows nt 6.3': '8.1',\r\n 'windows nt 6.2': '8',\r\n 'windows nt 6.1': '7',\r\n};\r\n\r\nconst getWindowsVersion = (ua: string): string => {\r\n for (const [key, version] of Object.entries(windowsVersionMap)) {\r\n if (ua.includes(key)) return version;\r\n }\r\n return '';\r\n};\r\n\r\nconst osDetectors = [\r\n {\r\n detect: (ua: string) => ua.includes('windows nt'),\r\n name: 'Windows',\r\n getVersion: (ua: string) => getWindowsVersion(ua),\r\n },\r\n {\r\n detect: (ua: string) => ua.includes('windows'),\r\n name: 'Windows',\r\n getVersion: () => '',\r\n },\r\n {\r\n detect: (ua: string) => ua.includes('iphone'),\r\n name: 'iOS',\r\n getVersion: (_ua: string, userAgent: string) => {\r\n const match = /OS (\\d+)_/i.exec(userAgent);\r\n return match ? match[1] : '';\r\n },\r\n },\r\n {\r\n detect: (ua: string) => ua.includes('ipad'),\r\n name: 'iPadOS',\r\n getVersion: (_ua: string, userAgent: string) => {\r\n const match = /OS (\\d+)_/i.exec(userAgent);\r\n return match ? match[1] : '';\r\n },\r\n },\r\n {\r\n detect: (ua: string) => ua.includes('mac os x'),\r\n name: 'macOS',\r\n getVersion: (_ua: string, userAgent: string) => {\r\n const match = /Mac OS X (\\d+)[._](\\d+)/i.exec(userAgent);\r\n return match ? `${match[1]}.${match[2]}` : '';\r\n },\r\n },\r\n {\r\n detect: (ua: string) => ua.includes('android'),\r\n name: 'Android',\r\n getVersion: (_ua: string, userAgent: string) => {\r\n const match = /Android (\\d+)/i.exec(userAgent);\r\n return match ? match[1] : '';\r\n },\r\n },\r\n {\r\n detect: (ua: string) => ua.includes('linux'),\r\n name: 'Linux',\r\n getVersion: () => '',\r\n },\r\n {\r\n detect: (ua: string) => ua.includes('cros'),\r\n name: 'Chrome OS',\r\n getVersion: () => '',\r\n },\r\n];\r\n\r\nconst detectOS = (ua: string, userAgent: string): { os: string; version: string } => {\r\n const detector = osDetectors.find(d => d.detect(ua));\r\n if (detector) {\r\n const version = detector.getVersion(ua, userAgent);\r\n return { os: detector.name, version };\r\n }\r\n return { os: 'Unknown', version: '' };\r\n};\r\n\r\nconst detectDeviceTypeFromUa = (ua: string): string => {\r\n const tabletRegex = /(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk)/i;\r\n if (tabletRegex.test(ua)) return 'Tablet';\r\n const mobileRegex = /(android|webos|iphone|ipod|blackberry|iemobile|opera mini|mobile|phone)/i;\r\n if (mobileRegex.test(ua)) return 'Mobile';\r\n return 'Desktop';\r\n};\r\n\r\nexport function parseUserAgent(userAgent: string | null | undefined): ParsedUserAgent {\r\n if (!userAgent) {\r\n return {\r\n browser: 'Unknown',\r\n browserVersion: '',\r\n os: 'Unknown',\r\n osVersion: '',\r\n device: 'Unknown',\r\n formatted: 'Unknown'\r\n };\r\n }\r\n\r\n const ua = userAgent.toLowerCase();\r\n const { browser, version: browserVersion } = detectBrowser(ua, userAgent);\r\n const { os, version: osVersion } = detectOS(ua, userAgent);\r\n const device = detectDeviceTypeFromUa(ua);\r\n\r\n let formatted = browser;\r\n if (os !== 'Unknown') {\r\n const versionPart = browserVersion ? browserVersion + ' ' : '';\r\n const osSuffix = osVersion ? ' ' + osVersion : '';\r\n formatted = `${browser} ${versionPart}sur ${os}${osSuffix}`;\r\n }\r\n\r\n return {\r\n browser,\r\n browserVersion,\r\n os,\r\n osVersion,\r\n device,\r\n formatted\r\n };\r\n}\r\n\r\n/**\r\n * Gets a short device description (icon-friendly)\r\n * @param userAgent The User-Agent string\r\n * @returns Short description like \"Chrome/Windows\" or \"Safari/iOS\"\r\n */\r\nexport function getDeviceShortName(userAgent: string | null | undefined): string {\r\n const parsed = parseUserAgent(userAgent);\r\n if (parsed.browser === 'Unknown' && parsed.os === 'Unknown') {\r\n return 'Unknown';\r\n }\r\n return `${parsed.browser}/${parsed.os}`;\r\n}\r\n\r\n/**\r\n * Detects the device type based on User-Agent string\r\n * @returns 'desktop' | 'mobile' | 'tablet' | 'unknown'\r\n */\r\nexport function detectDeviceType(): string {\r\n const userAgent = navigator.userAgent.toLowerCase();\r\n\r\n // Check for tablet first (more specific)\r\n const isTablet = /(ipad|tablet|(android(?!.*mobile))|(windows(?!.*phone)(.*touch))|kindle|playbook|silk|(puffin(?!.*(IP|AP|WP))))/.test(userAgent);\r\n\r\n if (isTablet) {\r\n return 'tablet';\r\n }\r\n\r\n // Check for mobile\r\n const isMobile = /(android|webos|iphone|ipod|blackberry|iemobile|opera mini|mobile|phone)/.test(userAgent);\r\n\r\n if (isMobile) {\r\n return 'mobile';\r\n }\r\n\r\n // Check for desktop\r\n const isDesktop = /(windows|macintosh|linux|x11)/.test(userAgent);\r\n\r\n if (isDesktop) {\r\n return 'desktop';\r\n }\r\n\r\n // Default to unknown\r\n return 'unknown';\r\n}\r\n","/**\r\n * Fetches the client's public IP address from an external service\r\n * @returns The public IP address or null if unable to fetch\r\n */\r\nexport async function getClientIpAddress(): Promise<string | null> {\r\n try {\r\n const response = await fetch('https://api.ipify.org?format=json', {\r\n method: 'GET',\r\n signal: AbortSignal.timeout(5000) // 5 second timeout\r\n });\r\n\r\n if (!response.ok) {\r\n return null;\r\n }\r\n\r\n const data = await response.json();\r\n return data.ip || null;\r\n } catch (error) {\r\n // If the service is unavailable, return null\r\n // The backend will use RemoteIpAddress as fallback\r\n return null;\r\n }\r\n}\r\n","import React, { useState, useCallback } from 'react';\r\nimport { api } from '@/services/api/apiClient';\r\nimport { userApi } from '@/services/api/userApi';\r\nimport { logService } from '@/services/logging/logService';\r\nimport { AuthenticationError, NetworkError, type LoginErrorCode } from '@/types/errors';\r\nimport { detectDeviceType } from '@/utils/deviceDetection';\r\nimport { getClientIpAddress } from '@/utils/ipDetection';\r\n\r\ninterface LoginError {\r\n message: string;\r\n code?: LoginErrorCode | 'NETWORK_ERROR';\r\n}\r\n\r\nexport function useLoginForm(): {\r\n email: string;\r\n setEmail: React.Dispatch<React.SetStateAction<string>>;\r\n password: string;\r\n setPassword: React.Dispatch<React.SetStateAction<string>>;\r\n showPassword: boolean;\r\n setShowPassword: React.Dispatch<React.SetStateAction<boolean>>;\r\n isLoading: boolean;\r\n loadingStep: 'auth' | 'prefs';\r\n error: LoginError | null;\r\n handleSubmit: (e: React.FormEvent) => Promise<void>;\r\n} {\r\n const [email, setEmail] = useState('');\r\n const [password, setPassword] = useState('');\r\n const [showPassword, setShowPassword] = useState(false);\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [loadingStep, setLoadingStep] = useState<'auth' | 'prefs'>('auth');\r\n const [error, setError] = useState<LoginError | null>(null);\r\n\r\n // External app SSO: capture redirect_uri on mount and persist in sessionStorage\r\n const [redirectUri] = useState<string | null>(() => {\r\n const fromUrl = new URLSearchParams(window.location.search).get('redirect_uri');\r\n if (fromUrl) {\r\n sessionStorage.setItem('sso_redirect_uri', fromUrl);\r\n return fromUrl;\r\n }\r\n return sessionStorage.getItem('sso_redirect_uri');\r\n });\r\n\r\n const handleSubmit = useCallback(\r\n async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setError(null);\r\n setLoadingStep('auth');\r\n setIsLoading(true);\r\n\r\n try {\r\n logService.logEvent('login_attempt', 'auth', { email });\r\n\r\n const deviceType = detectDeviceType();\r\n const clientIp = await getClientIpAddress();\r\n const data = await api.post<{ token: string; refreshToken: string; user: unknown }>(\r\n '/api/auth/login',\r\n { email, password, deviceType, clientIp }\r\n );\r\n\r\n localStorage.setItem('token', data.token);\r\n localStorage.setItem('refreshToken', data.refreshToken);\r\n localStorage.setItem('user', JSON.stringify(data.user));\r\n\r\n logService.logEvent('login_success', 'auth', { email });\r\n setLoadingStep('prefs');\r\n\r\n // Check if user needs onboarding\r\n try {\r\n const onboardingStatus = await api.get<{ hasCompletedOnboarding: boolean }>(\r\n '/api/onboarding/status'\r\n );\r\n if (!onboardingStatus.hasCompletedOnboarding) {\r\n window.location.href = '/auth/onboarding';\r\n return;\r\n }\r\n } catch {\r\n // Non-critical - continue with normal flow\r\n }\r\n\r\n try {\r\n const prefs = await userApi.preferences.get();\r\n const themeConfig = {\r\n mode: prefs.theme || 'dark',\r\n accentColorKey: prefs.accentColorKey || 'indigo',\r\n borderRadius: prefs.borderRadius || {},\r\n itemPaletteKey: prefs.itemPaletteKey || 'neutral',\r\n };\r\n localStorage.setItem('smartstack-theme-config', JSON.stringify(themeConfig));\r\n if (prefs.language) {\r\n localStorage.setItem('i18nextLng', prefs.language);\r\n }\r\n } catch {\r\n // Non critique - continuer avec les valeurs par défaut\r\n }\r\n\r\n // Clear stale tenant state so TenantContext uses the user's configured default tenant (Priority 3: isDefault)\r\n localStorage.removeItem('currentTenantId');\r\n localStorage.removeItem('currentTenantSlug');\r\n localStorage.removeItem('isGlobalView');\r\n\r\n // External app SSO: redirect back to the calling app with tokens\r\n if (redirectUri) {\r\n sessionStorage.removeItem('sso_redirect_uri');\r\n const separator = redirectUri.includes('?') ? '&' : '?';\r\n window.location.href = `${redirectUri}${separator}token=${encodeURIComponent(\r\n data.token\r\n )}&refreshToken=${encodeURIComponent(data.refreshToken)}`;\r\n return;\r\n }\r\n\r\n window.location.href = '/myspace';\r\n } catch (err) {\r\n if (err instanceof NetworkError) {\r\n setError({ message: err.message, code: 'NETWORK_ERROR' });\r\n } else if (err instanceof AuthenticationError) {\r\n setError({ message: err.message, code: err.code });\r\n } else {\r\n const message = err instanceof Error ? err.message : 'Une erreur inattendue est survenue';\r\n setError({ message });\r\n }\r\n logService.logError(\r\n err instanceof Error ? err : new Error(String(err)),\r\n 'LoginPage.handleSubmit',\r\n { email }\r\n );\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n },\r\n [email, password, redirectUri]\r\n );\r\n\r\n return {\r\n email,\r\n setEmail,\r\n password,\r\n setPassword,\r\n showPassword,\r\n setShowPassword,\r\n isLoading,\r\n loadingStep,\r\n error,\r\n handleSubmit,\r\n };\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { Eye, EyeOff, Lock, Mail, Loader2, Layers, Shield, Zap, Users, BarChart3, AlertCircle, UserX, Clock, KeyRound, WifiOff, Home } from 'lucide-react';\r\nimport { ThemeSwitcher } from '@/components/ui/ThemeSwitcher';\r\nimport { useLoginForm } from '@/hooks/useLoginForm';\r\nimport type { LoginErrorCode } from '@/types/errors';\r\n\r\nconst features = [\r\n {\r\n icon: Shield,\r\n title: 'Sécurité avancée',\r\n description: 'Authentification multi-facteurs et contrôle d\\'accès granulaire'\r\n },\r\n {\r\n icon: Zap,\r\n title: 'Performance optimale',\r\n description: 'Interface rapide et réactive pour une productivité maximale'\r\n },\r\n {\r\n icon: Users,\r\n title: 'Collaboration',\r\n description: 'Gestion des équipes et permissions centralisée'\r\n },\r\n {\r\n icon: BarChart3,\r\n title: 'Analytics',\r\n description: 'Tableaux de bord et rapports en temps réel'\r\n }\r\n];\r\n\r\nconst getErrorIcon = (code?: LoginErrorCode | 'NETWORK_ERROR') => {\r\n switch (code) {\r\n case 'ACCOUNT_DISABLED':\r\n return <UserX className=\"w-5 h-5 flex-shrink-0\" />;\r\n case 'ACCOUNT_LOCKED':\r\n return <Clock className=\"w-5 h-5 flex-shrink-0\" />;\r\n case 'EXTERNAL_AUTH_REQUIRED':\r\n return <KeyRound className=\"w-5 h-5 flex-shrink-0\" />;\r\n case 'NETWORK_ERROR':\r\n return <WifiOff className=\"w-5 h-5 flex-shrink-0\" />;\r\n default:\r\n return <AlertCircle className=\"w-5 h-5 flex-shrink-0\" />;\r\n }\r\n};\r\n\r\nconst getErrorStyle = (code?: LoginErrorCode | 'NETWORK_ERROR') => {\r\n switch (code) {\r\n case 'ACCOUNT_DISABLED':\r\n return 'bg-orange-500/10 border-orange-500/20 text-orange-600 dark:text-orange-400';\r\n case 'ACCOUNT_LOCKED':\r\n return 'bg-amber-500/10 border-amber-500/20 text-amber-600 dark:text-amber-400';\r\n case 'EXTERNAL_AUTH_REQUIRED':\r\n return 'bg-blue-500/10 border-blue-500/20 text-blue-600 dark:text-blue-400';\r\n case 'NETWORK_ERROR':\r\n return 'bg-gray-500/10 border-gray-500/20 text-gray-600 dark:text-gray-400';\r\n default:\r\n return 'bg-red-500/10 border-red-500/20 text-red-500';\r\n }\r\n};\r\n\r\nexport function LoginPage(): ReactElement {\r\n const {\r\n email,\r\n setEmail,\r\n password,\r\n setPassword,\r\n showPassword,\r\n setShowPassword,\r\n isLoading,\r\n loadingStep,\r\n error,\r\n handleSubmit,\r\n } = useLoginForm();\r\n\r\n return (\r\n <div className=\"min-h-screen flex\">\r\n {/* Left Panel - Branding (hidden on mobile) */}\r\n <div className=\"hidden lg:flex lg:w-1/2 xl:w-[55%] relative overflow-hidden\">\r\n {/* Animated background */}\r\n <div className=\"absolute inset-0 animated-gradient\" />\r\n\r\n {/* Overlay pattern */}\r\n <div\r\n className=\"absolute inset-0 opacity-10\"\r\n style={{\r\n backgroundImage: `radial-gradient(circle at 1px 1px, white 1px, transparent 0)`,\r\n backgroundSize: '40px 40px'\r\n }}\r\n />\r\n\r\n {/* Floating shapes */}\r\n <div className=\"absolute top-20 left-20 w-72 h-72 bg-white/10 rounded-full blur-3xl animate-pulse\" />\r\n <div className=\"absolute bottom-20 right-20 w-96 h-96 bg-white/10 rounded-full blur-3xl animate-pulse\" style={{ animationDelay: '1s' }} />\r\n <div className=\"absolute top-1/2 left-1/3 w-64 h-64 bg-white/5 rounded-full blur-2xl animate-pulse\" style={{ animationDelay: '2s' }} />\r\n\r\n {/* Content */}\r\n <div className=\"relative z-10 flex flex-col justify-between p-12 xl:p-16 text-white w-full\">\r\n {/* Logo */}\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"w-12 h-12 bg-white/20 backdrop-blur-sm rounded-xl flex items-center justify-center\">\r\n <Layers className=\"w-7 h-7 text-white\" />\r\n </div>\r\n <span className=\"text-2xl font-bold\">SmartStack</span>\r\n </div>\r\n\r\n {/* Main content */}\r\n <div className=\"max-w-lg\">\r\n <h1 className=\"text-4xl xl:text-5xl font-bold leading-tight mb-6\">\r\n Votre plateforme de gestion unifiée\r\n </h1>\r\n <p className=\"text-xl text-white/80 mb-12\">\r\n Simplifiez vos processus, centralisez vos données et boostez la productivité de votre équipe.\r\n </p>\r\n\r\n {/* Features grid */}\r\n <div className=\"grid grid-cols-2 gap-6\">\r\n {features.map((feature) => (\r\n <div\r\n key={feature.title}\r\n className=\"bg-white/10 backdrop-blur-sm rounded-2xl p-5 hover:bg-white/15 transition-colors\"\r\n >\r\n <feature.icon className=\"w-8 h-8 mb-3 text-white/90\" />\r\n <h3 className=\"font-semibold mb-1\">{feature.title}</h3>\r\n <p className=\"text-sm text-white/70\">{feature.description}</p>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Footer */}\r\n <p className=\"text-sm text-white/60\">\r\n &copy; {new Date().getFullYear()} SmartStack &mdash; Plateforme de gestion unifiée\r\n </p>\r\n </div>\r\n </div>\r\n\r\n {/* Right Panel - Login Form */}\r\n <div className=\"w-full lg:w-1/2 xl:w-[45%] flex flex-col relative bg-[var(--bg-primary)]\">\r\n {/* Mobile background (visible only on mobile) */}\r\n <div className=\"lg:hidden absolute inset-0\">\r\n <div className=\"absolute inset-0 overflow-hidden\">\r\n <div className=\"absolute -top-40 -right-40 w-96 h-96 rounded-full bg-primary-500/20 blur-3xl animate-pulse\" />\r\n <div className=\"absolute -bottom-40 -left-40 w-96 h-96 rounded-full bg-accent-500/20 blur-3xl animate-pulse\" style={{ animationDelay: '1s' }} />\r\n </div>\r\n </div>\r\n\r\n {/* Navigation buttons */}\r\n <div className=\"absolute top-6 right-6 z-10 flex items-center gap-2\">\r\n <a\r\n href=\"/\"\r\n className=\"p-2 rounded-lg bg-[var(--bg-tertiary)] hover:bg-[var(--bg-secondary)] border border-[var(--border-color)] transition-colors\"\r\n title=\"Retour à l'accueil\"\r\n >\r\n <Home className=\"w-5 h-5 text-[var(--text-secondary)]\" />\r\n </a>\r\n <ThemeSwitcher />\r\n </div>\r\n\r\n {/* Form container */}\r\n <div className=\"flex-1 flex items-center justify-center p-6 sm:p-8 lg:p-12\">\r\n <div className=\"w-full max-w-md relative\">\r\n {/* Logo (mobile only) */}\r\n <div className=\"lg:hidden flex flex-col items-center mb-8\">\r\n <div className=\"w-16 h-16 rounded-2xl animated-gradient flex items-center justify-center mb-4 shadow-lg\">\r\n <Layers className=\"w-8 h-8 text-white\" />\r\n </div>\r\n <h1 className=\"text-2xl font-bold\">SmartStack</h1>\r\n </div>\r\n\r\n {/* Desktop title */}\r\n <div className=\"hidden lg:block mb-8\">\r\n <h2 className=\"text-3xl font-bold mb-2\">Bienvenue</h2>\r\n <p className=\"text-[var(--text-secondary)]\">Connectez-vous à votre compte pour continuer</p>\r\n </div>\r\n\r\n {/* Error message */}\r\n {error && (\r\n <div className={`mb-6 p-4 rounded-xl border text-sm flex items-start gap-3 ${getErrorStyle(error.code)}`}>\r\n {getErrorIcon(error.code)}\r\n <div className=\"flex-1\">\r\n <p className=\"font-medium\">{error.message}</p>\r\n {error.code === 'ACCOUNT_DISABLED' && (\r\n <p className=\"text-xs mt-1 opacity-80\">\r\n Contactez le support si vous pensez qu'il s'agit d'une erreur.\r\n </p>\r\n )}\r\n {error.code === 'EXTERNAL_AUTH_REQUIRED' && (\r\n <p className=\"text-xs mt-1 opacity-80\">\r\n Utilisez les boutons Microsoft ou Google ci-dessous.\r\n </p>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Form */}\r\n <form onSubmit={handleSubmit} className=\"space-y-5\">\r\n {/* Email field */}\r\n <div>\r\n <label htmlFor=\"email\" className=\"block text-sm font-medium mb-2\">\r\n Adresse email\r\n </label>\r\n <div className=\"relative\">\r\n <Mail className=\"absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-[var(--text-tertiary)]\" />\r\n <input\r\n id=\"email\"\r\n type=\"email\"\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n placeholder=\"vous@exemple.com\"\r\n required\r\n className=\"w-full pl-12 pr-4 py-3.5 rounded-xl bg-[var(--bg-tertiary)] border border-[var(--border-color)] focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 outline-none transition-all placeholder:text-[var(--text-tertiary)]\"\r\n />\r\n </div>\r\n </div>\r\n\r\n {/* Password field */}\r\n <div>\r\n <label htmlFor=\"password\" className=\"block text-sm font-medium mb-2\">\r\n Mot de passe\r\n </label>\r\n <div className=\"relative\">\r\n <Lock className=\"absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-[var(--text-tertiary)]\" />\r\n <input\r\n id=\"password\"\r\n type={showPassword ? 'text' : 'password'}\r\n value={password}\r\n onChange={(e) => setPassword(e.target.value)}\r\n placeholder=\"••••••••\"\r\n required\r\n className=\"w-full pl-12 pr-12 py-3.5 rounded-xl bg-[var(--bg-tertiary)] border border-[var(--border-color)] focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 outline-none transition-all placeholder:text-[var(--text-tertiary)]\"\r\n />\r\n <button\r\n type=\"button\"\r\n onClick={() => setShowPassword(!showPassword)}\r\n className=\"absolute right-4 top-1/2 -translate-y-1/2 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] transition-colors\"\r\n >\r\n {showPassword ? <EyeOff className=\"w-5 h-5\" /> : <Eye className=\"w-5 h-5\" />}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {/* Remember me & Forgot password */}\r\n <div className=\"flex items-center justify-between text-sm\">\r\n <label className=\"flex items-center gap-2 cursor-pointer\">\r\n <input\r\n type=\"checkbox\"\r\n className=\"w-4 h-4 rounded border-[var(--border-color)] bg-[var(--bg-tertiary)] text-primary-600 focus:ring-primary-500/20\"\r\n />\r\n <span className=\"text-[var(--text-secondary)]\">Se souvenir de moi</span>\r\n </label>\r\n <a href=\"/forgot-password\" className=\"text-primary-500 hover:text-primary-600 font-medium transition-colors\">\r\n Mot de passe oublié ?\r\n </a>\r\n </div>\r\n\r\n {/* Submit button */}\r\n <button\r\n type=\"submit\"\r\n disabled={isLoading}\r\n className=\"w-full py-3.5 px-4 rounded-xl bg-primary-600 hover:bg-primary-700 disabled:bg-primary-600/50 text-white font-semibold transition-all shadow-lg shadow-primary-500/25 hover:shadow-primary-500/40 disabled:shadow-none flex items-center justify-center gap-2\"\r\n >\r\n {isLoading ? (\r\n <>\r\n <Loader2 className=\"w-5 h-5 animate-spin\" />\r\n {loadingStep === 'auth' ? 'Connexion en cours...' : 'Chargement des préférences...'}\r\n </>\r\n ) : (\r\n 'Se connecter'\r\n )}\r\n </button>\r\n </form>\r\n\r\n {/* Divider - Social login */}\r\n <div className=\"relative my-8\">\r\n <div className=\"absolute inset-0 flex items-center\">\r\n <div className=\"w-full border-t border-[var(--border-color)]\" />\r\n </div>\r\n <div className=\"relative flex justify-center text-sm\">\r\n <span className=\"px-4 bg-[var(--bg-primary)] text-[var(--text-tertiary)]\">\r\n Ou continuer avec\r\n </span>\r\n </div>\r\n </div>\r\n\r\n {/* Social login buttons */}\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <button\r\n type=\"button\"\r\n onClick={() => window.location.href = '/api/auth/microsoft'}\r\n className=\"flex items-center justify-center gap-3 py-3.5 px-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-secondary)] border border-[var(--border-color)] font-medium transition-all\"\r\n >\r\n <svg className=\"w-5 h-5\" viewBox=\"0 0 21 21\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <rect x=\"1\" y=\"1\" width=\"9\" height=\"9\" fill=\"#F25022\"/>\r\n <rect x=\"11\" y=\"1\" width=\"9\" height=\"9\" fill=\"#7FBA00\"/>\r\n <rect x=\"1\" y=\"11\" width=\"9\" height=\"9\" fill=\"#00A4EF\"/>\r\n <rect x=\"11\" y=\"11\" width=\"9\" height=\"9\" fill=\"#FFB900\"/>\r\n </svg>\r\n Microsoft\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => window.location.href = '/api/auth/google'}\r\n className=\"flex items-center justify-center gap-3 py-3.5 px-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-secondary)] border border-[var(--border-color)] font-medium transition-all\"\r\n >\r\n <svg className=\"w-5 h-5\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z\" fill=\"#4285F4\"/>\r\n <path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" fill=\"#34A853\"/>\r\n <path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" fill=\"#FBBC05\"/>\r\n <path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" fill=\"#EA4335\"/>\r\n </svg>\r\n Google\r\n </button>\r\n </div>\r\n\r\n {/* Register link */}\r\n <p className=\"text-center text-[var(--text-secondary)] mt-8\">\r\n Nouveau sur SmartStack ?{' '}\r\n <a href=\"/register\" className=\"text-primary-500 hover:text-primary-600 font-semibold transition-colors\">\r\n Créer un compte\r\n </a>\r\n </p>\r\n </div>\r\n </div>\r\n\r\n {/* Footer */}\r\n <div className=\"p-6 text-center\">\r\n <p className=\"text-sm text-[var(--text-tertiary)]\">\r\n &copy; {new Date().getFullYear()} SmartStack. Tous droits réservés.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useNavigate } from 'react-router-dom';\r\nimport { FileWarning, ArrowLeft, Home } from 'lucide-react';\r\n\r\nexport function NotFoundPage(): ReactElement {\r\n const { t } = useTranslation('common');\r\n const navigate = useNavigate();\r\n\r\n const handleGoBack = () => {\r\n if (window.history.length > 2) {\r\n navigate(-1);\r\n } else {\r\n navigate('/');\r\n }\r\n };\r\n\r\n return (\r\n <div className=\"min-h-screen flex items-center justify-center bg-[var(--bg-primary)] p-4\">\r\n <div className=\"max-w-md w-full p-8 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-2xl shadow-xl\">\r\n <div className=\"flex flex-col items-center text-center\">\r\n <div className=\"w-16 h-16 rounded-full bg-yellow-500/10 flex items-center justify-center mb-6\">\r\n <FileWarning className=\"w-8 h-8 text-yellow-500\" />\r\n </div>\r\n\r\n <h1 className=\"text-2xl font-bold text-[var(--text-primary)] mb-2\">\r\n {t('errors.notFound.title')}\r\n </h1>\r\n\r\n <p className=\"text-[var(--text-secondary)] mb-6\">\r\n {t('errors.notFound.description')}\r\n </p>\r\n\r\n <p className=\"text-sm text-[var(--text-tertiary)] mb-6 font-mono\">\r\n {window.location.pathname}\r\n </p>\r\n\r\n <div className=\"flex flex-col sm:flex-row gap-3 w-full\">\r\n <button\r\n onClick={handleGoBack}\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--color-accent-600)] hover:bg-[var(--color-accent-700)] text-white font-medium transition-all\"\r\n >\r\n <ArrowLeft className=\"w-4 h-4\" />\r\n {t('errors.goBack')}\r\n </button>\r\n\r\n <a\r\n href=\"/\"\r\n className=\"flex-1 flex items-center justify-center gap-2 py-3 px-4 rounded-xl bg-[var(--bg-tertiary)] hover:bg-[var(--bg-hover)] border border-[var(--border-color)] text-[var(--text-primary)] font-medium transition-all\"\r\n >\r\n <Home className=\"w-4 h-4\" />\r\n {t('errors.home')}\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { lazy, Suspense, useMemo } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { Routes, Route, Navigate, useLocation } from 'react-router-dom';\r\nimport { useNavigation, type ApplicationDto } from '@/contexts/NavigationContext';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { PageRegistry } from '@/extensions/PageRegistry';\r\nimport { TenantRouteWrapper } from './TenantRouteWrapper';\r\nimport { TenantNavigate } from './TenantNavigate';\r\nimport { ProtectedRoute } from './ProtectedRoute';\r\nimport { LicenseGuard } from '@/components/license/LicenseGuard';\r\nimport { FeatureRouteGuard } from '@/components/license/FeatureRouteGuard';\r\nimport { PageLoader } from '@/components/ui/PageLoader';\r\nimport { PageTracker } from '@/components/tracking/PageTracker';\r\nimport { TenantTracker } from '@/components/tracking/TenantTracker';\r\nimport { AccessDenied } from '@/components/errors/AccessDenied';\r\nimport { AppHeader } from '@/components/layout/AppHeader';\r\nimport type { RegisterableComponent } from '@/extensions/types';\r\nimport { DocRoutes } from './DocRoutes';\r\nimport { OUTLET_SECTIONS } from '@/config/outletSections';\r\n\r\n// Critical pages - kept static for fast initial load (never from registry)\r\nimport { HomePage } from '@/pages/HomePage';\r\nimport { LoginPage } from '@/pages/LoginPage';\r\nimport { NotFoundPage } from '@/pages/NotFoundPage';\r\n\r\n// System pages — always available, not from DB navigation\r\nconst ApplicationsPage = lazy(() => import('@/pages/ApplicationsPage').then(m => ({ default: m.ApplicationsPage })));\r\nconst NotificationsPage = lazy(() => import('@/pages/notifications/NotificationsPage').then(m => ({ default: m.NotificationsPage })));\r\n\r\n// ---------------------------------------------------------------------------\r\n// Layouts\r\n// ---------------------------------------------------------------------------\r\nconst PublicLayout = lazy(() => import('@/layouts/PublicLayout').then(m => ({ default: m.PublicLayout })));\r\nconst AppLayout = lazy(() => import('@/layouts/AppLayout').then(m => ({ default: m.AppLayout })));\r\n\r\n// ---------------------------------------------------------------------------\r\n// Auth pages — always static (not from DB navigation)\r\n// ---------------------------------------------------------------------------\r\nconst RegisterPage = lazy(() => import('@/pages/auth/RegisterPage').then(m => ({ default: m.RegisterPage })));\r\nconst ConfirmEmailPage = lazy(() => import('@/pages/auth/ConfirmEmailPage').then(m => ({ default: m.ConfirmEmailPage })));\r\nconst ForgotPasswordPage = lazy(() => import('@/pages/auth/ForgotPasswordPage').then(m => ({ default: m.ForgotPasswordPage })));\r\nconst ResetPasswordPage = lazy(() => import('@/pages/auth/ResetPasswordPage').then(m => ({ default: m.ResetPasswordPage })));\r\nconst ForceChangePasswordPage = lazy(() => import('@/pages/auth/ForceChangePasswordPage').then(m => ({ default: m.ForceChangePasswordPage })));\r\nconst AuthCallbackPage = lazy(() => import('@/pages/auth/AuthCallbackPage').then(m => ({ default: m.AuthCallbackPage })));\r\nconst OnboardingWizardPage = lazy(() => import('@/pages/auth/OnboardingWizardPage').then(m => ({ default: m.OnboardingWizardPage })));\r\n\r\n// ===========================================================================\r\n// Route generation helpers\r\n// ===========================================================================\r\n\r\n/**\r\n * Convention suffixes for implicit routes (not in DB).\r\n * When a page key ends with one of these suffixes, the DynamicRouter\r\n * generates the corresponding URL pattern automatically.\r\n *\r\n * Two categories:\r\n * 1. Entity sub-pages (prefixed with /:id) — detail views of a specific entity\r\n * 2. Collection-level pages (no :id) — actions on the list/module itself\r\n *\r\n * To use: register a page in PageRegistry with key `{parentKey}{suffix}`.\r\n * Example: `administration.tenants.settings` → route `tenants/:id/settings`\r\n *\r\n * If a suffix collides with a DB section code, React Router's specificity\r\n * rules ensure the static DB route wins for exact matches (e.g. `ai/settings`\r\n * wins over `ai/:id/settings` when the segment is literally \"settings\").\r\n */\r\nconst IMPLICIT_SUFFIXES: Record<string, string> = {\r\n // ── Entity identification ──\r\n '.detail': '/:id',\r\n\r\n // ── Entity CRUD sub-pages ──\r\n '.edit': '/:id/edit',\r\n '.create': '/create',\r\n '.new': '/new',\r\n '.duplicate': '/:id/duplicate',\r\n\r\n // ── Entity sub-pages (common patterns) ──\r\n '.settings': '/:id/settings',\r\n '.configure': '/:id/configure',\r\n '.permissions': '/:id/permissions',\r\n '.members': '/:id/members',\r\n '.history': '/:id/history',\r\n '.logs': '/:id/logs',\r\n '.analytics': '/:id/analytics',\r\n '.preview': '/:id/preview',\r\n '.versions': '/:id/versions',\r\n '.comments': '/:id/comments',\r\n '.attachments': '/:id/attachments',\r\n '.audit': '/:id/audit',\r\n '.export': '/:id/export',\r\n '.notifications': '/:id/notifications',\r\n '.schedule': '/:id/schedule',\r\n '.workflow': '/:id/workflow',\r\n '.summary': '/:id/summary',\r\n '.test': '/:id/test',\r\n '.runs': '/:id/runs',\r\n\r\n // ── Collection-level pages ──\r\n '.import': '/import',\r\n};\r\n\r\n// OUTLET_SECTIONS imported from @/config/outletSections\r\n\r\n/**\r\n * Resolves implicit routes for a given base componentKey.\r\n * E.g., if \"administration.users\" is a DB route at \"/administration/users\",\r\n * and \"administration.users.detail\" exists in PageRegistry,\r\n * this generates a route at \"/administration/users/:id\".\r\n */\r\nfunction getImplicitRoutes(baseKey: string, baseRoute: string): Array<{ path: string; Component: RegisterableComponent }> {\r\n const routes: Array<{ path: string; Component: RegisterableComponent }> = [];\r\n\r\n for (const [suffix, urlPattern] of Object.entries(IMPLICIT_SUFFIXES)) {\r\n const implicitKey = `${baseKey}${suffix}`;\r\n const component = PageRegistry.resolve(implicitKey);\r\n if (component) {\r\n // Strip leading slash for relative path matching\r\n const relativePath = baseRoute.replace(/^\\//, '') + urlPattern;\r\n routes.push({ path: relativePath, Component: component });\r\n }\r\n }\r\n\r\n return routes;\r\n}\r\n\r\n/**\r\n * Build route elements for a single resource and its implicit routes.\r\n * Handles detail, create, edit implicit suffixes.\r\n */\r\nfunction buildResourceRoutes(\r\n mod: any,\r\n section: any,\r\n resource: any,\r\n appCode: string,\r\n): ReactElement[] {\r\n const routes: ReactElement[] = [];\r\n const resourceRelRoute = resource.route?.replace(`/${appCode}/`, '') ||\r\n `${mod.code}/${section.code}/${resource.code}`;\r\n\r\n const resComponent = PageRegistry.resolve(resource.componentKey);\r\n if (resComponent) {\r\n const ResComponent = resComponent;\r\n routes.push(<Route key={resource.componentKey} path={resourceRelRoute} element={<ResComponent />} />);\r\n }\r\n\r\n // Resource implicit routes (detail, create, edit)\r\n if (resource.route) {\r\n for (const implicit of getImplicitRoutes(resource.componentKey, resourceRelRoute)) {\r\n const ImplicitComponent = implicit.Component;\r\n routes.push(\r\n <Route key={`${resource.componentKey}-implicit-${implicit.path}`} path={implicit.path} element={<ImplicitComponent />} />\r\n );\r\n }\r\n }\r\n\r\n return routes;\r\n}\r\n\r\n/**\r\n * Build route elements for a single section and its resources.\r\n * Handles outlet-based tab sections, section component, and auto-redirects.\r\n */\r\nfunction buildSectionRoutes(\r\n mod: any,\r\n section: any,\r\n appCode: string,\r\n): ReactElement[] {\r\n const routes: ReactElement[] = [];\r\n const sectionRelRoute = section.route?.replace(`/${appCode}/`, '') || `${mod.code}/${section.code}`;\r\n\r\n // Check for Outlet-based tab sections (resolved from PageRegistry)\r\n const outletConfig = OUTLET_SECTIONS[section.componentKey];\r\n if (outletConfig) {\r\n const OutletComponent = PageRegistry.resolve(section.componentKey);\r\n if (OutletComponent) {\r\n const tabRoutes = outletConfig.tabs\r\n .map(tab => {\r\n const tabKey = `${section.componentKey}.${tab}`;\r\n const TabComponent = PageRegistry.resolve(tabKey);\r\n return TabComponent ? <Route key={tabKey} path={tab} element={<TabComponent />} /> : null;\r\n })\r\n .filter(Boolean);\r\n\r\n routes.push(\r\n <Route key={section.componentKey} path={sectionRelRoute} element={<OutletComponent />}>\r\n <Route index element={<Navigate to={outletConfig.defaultTab} replace />} />\r\n {tabRoutes}\r\n </Route>\r\n );\r\n return routes;\r\n }\r\n }\r\n\r\n const secComponent = PageRegistry.resolve(section.componentKey);\r\n if (secComponent) {\r\n const SecComponent = secComponent;\r\n routes.push(<Route key={section.componentKey} path={sectionRelRoute} element={<SecComponent />} />);\r\n }\r\n\r\n // Auto-redirect: section without component but with resources → redirect to first resource\r\n if (!secComponent && section.resources.length > 0) {\r\n const firstResource = section.resources[0];\r\n routes.push(\r\n <Route key={`${section.componentKey}-redirect`} path={sectionRelRoute}\r\n element={<Navigate to={firstResource.code} replace />} />\r\n );\r\n }\r\n\r\n // Section implicit routes\r\n if (section.route) {\r\n for (const implicit of getImplicitRoutes(section.componentKey, sectionRelRoute)) {\r\n const ImplicitComponent = implicit.Component;\r\n routes.push(\r\n <Route key={`${section.componentKey}-implicit-${implicit.path}`} path={implicit.path} element={<ImplicitComponent />} />\r\n );\r\n }\r\n }\r\n\r\n // Resources within section\r\n for (const resource of section.resources) {\r\n const resourceRoutes = buildResourceRoutes(mod, section, resource, appCode);\r\n routes.push(...resourceRoutes);\r\n }\r\n\r\n return routes;\r\n}\r\n\r\n/**\r\n * Build route elements for a single module and its sections.\r\n * Handles module component, auto-redirects, and section routes.\r\n */\r\nfunction buildModuleRoutes(\r\n mod: any,\r\n appCode: string,\r\n): ReactElement[] {\r\n const routes: ReactElement[] = [];\r\n const moduleRelRoute = mod.route?.replace(`/${appCode}/`, '') || mod.code;\r\n\r\n // Module component\r\n const modComponent = PageRegistry.resolve(mod.componentKey);\r\n if (modComponent) {\r\n const ModComponent = modComponent;\r\n routes.push(<Route key={mod.componentKey} path={moduleRelRoute} element={<ModComponent />} />);\r\n }\r\n\r\n // Auto-redirect: module without component but with sections → redirect to first section\r\n if (!modComponent && mod.sections.length > 0) {\r\n const firstSection = mod.sections[0];\r\n routes.push(\r\n <Route key={`${mod.componentKey}-redirect`} path={moduleRelRoute}\r\n element={<Navigate to={firstSection.code} replace />} />\r\n );\r\n }\r\n\r\n // Module implicit routes (detail, create, edit)\r\n if (mod.route) {\r\n for (const implicit of getImplicitRoutes(mod.componentKey, moduleRelRoute)) {\r\n const ImplicitComponent = implicit.Component;\r\n routes.push(\r\n <Route key={`${mod.componentKey}-implicit-${implicit.path}`} path={implicit.path} element={<ImplicitComponent />} />\r\n );\r\n }\r\n }\r\n\r\n // Sections within module\r\n for (const section of mod.sections) {\r\n const sectionRoutes = buildSectionRoutes(mod, section, appCode);\r\n routes.push(...sectionRoutes);\r\n }\r\n\r\n return routes;\r\n}\r\n\r\n/**\r\n * Build route elements for a single application and its children.\r\n * Handles application index, module routes, and application-level feature gating.\r\n */\r\nfunction buildApplicationRoutes(app: ApplicationDto): ReactElement[] {\r\n const routes: ReactElement[] = [];\r\n\r\n // Application index: render registered component or redirect to first module\r\n const appComponent = PageRegistry.resolve(app.componentKey);\r\n if (appComponent) {\r\n const AppComponent = appComponent;\r\n routes.push(<Route key={`${app.code}-index`} index element={<AppComponent />} />);\r\n } else if (app.modules.length > 0) {\r\n const firstModule = app.modules[0];\r\n const firstModuleRoute = firstModule.route?.replace(/^\\//, '').replace(new RegExp(`^${app.code}/?`), '') || firstModule.code;\r\n routes.push(<Route key={`${app.code}-index`} index element={<Navigate to={firstModuleRoute} replace />} />);\r\n }\r\n\r\n // Modules\r\n for (const mod of app.modules) {\r\n const moduleRoutes = buildModuleRoutes(mod, app.code);\r\n\r\n // Module-level feature gating\r\n if (mod.requiredFeature) {\r\n routes.push(\r\n <Route key={`feature-${mod.componentKey}`} element={<FeatureRouteGuard feature={mod.requiredFeature} />}>\r\n {moduleRoutes}\r\n </Route>\r\n );\r\n } else {\r\n routes.push(...moduleRoutes);\r\n }\r\n }\r\n\r\n return routes;\r\n}\r\n\r\n/**\r\n * Groups application routes, optionally wrapping feature-gated apps.\r\n */\r\nfunction wrapWithFeatureGuard(app: ApplicationDto, children: ReactElement[]): ReactElement[] {\r\n if (app.requiredFeature) {\r\n return [\r\n <Route key={`feature-${app.code}`} element={<FeatureRouteGuard feature={app.requiredFeature} />}>\r\n {children}\r\n </Route>\r\n ];\r\n }\r\n return children;\r\n}\r\n\r\n/**\r\n * Returns static legacy redirect routes for a specific application.\r\n * These are in-app redirects that cannot come from the DB menu.\r\n *\r\n * MAINTENANCE: These redirects exist for backwards-compatibility with\r\n * bookmarks and external links created before route restructuring.\r\n * Each redirect maps an old URL pattern to its new location.\r\n * Remove individual redirects only after confirming no active references.\r\n */\r\nfunction getStaticAppRoutes(appCode: string): ReactElement[] {\r\n switch (appCode) {\r\n case 'administration':\r\n return [\r\n // Legacy settings → configuration/settings\r\n <Route key=\"legacy-settings\" path=\"settings\" element={<Navigate to=\"../configuration/settings\" relative=\"path\" replace />} />,\r\n <Route key=\"legacy-settings-wc\" path=\"settings/*\" element={<TenantNavigate to=\"/administration/configuration/settings\" />} />,\r\n <Route key=\"legacy-appsettings\" path=\"appsettings\" element={<Navigate to=\"../configuration/appsettings\" relative=\"path\" replace />} />,\r\n <Route key=\"legacy-appsettings-wc\" path=\"appsettings/*\" element={<TenantNavigate to=\"/administration/configuration/appsettings\" />} />,\r\n // Legacy email-templates → workflows/email-templates\r\n <Route key=\"legacy-email-templates\" path=\"email-templates\" element={<Navigate to=\"../workflows/email-templates\" relative=\"path\" replace />} />,\r\n <Route key=\"legacy-email-templates-list\" path=\"email-templates/list\" element={<Navigate to=\"../../workflows/email-templates\" relative=\"path\" replace />} />,\r\n <Route key=\"legacy-email-templates-create\" path=\"email-templates/create\" element={<Navigate to=\"../../workflows/email-templates/create\" relative=\"path\" replace />} />,\r\n // Legacy workflows/templates → workflows/email-templates\r\n <Route key=\"legacy-wf-templates\" path=\"workflows/templates\" element={<Navigate to=\"../email-templates\" relative=\"path\" replace />} />,\r\n <Route key=\"legacy-wf-templates-wc\" path=\"workflows/templates/*\" element={<TenantNavigate to=\"/administration/workflows/email-templates\" />} />,\r\n // Legacy tenants sub-routes\r\n <Route key=\"legacy-tenants-biz\" path=\"tenants/business\" element={<Navigate to=\"../tenants/list/enterprise\" replace />} />,\r\n <Route key=\"legacy-tenants-perso\" path=\"tenants/personal\" element={<Navigate to=\"../tenants/list/personal\" replace />} />,\r\n ];\r\n case 'support':\r\n return [\r\n // Legacy support redirects\r\n <Route key=\"legacy-dashboard\" path=\"dashboard\" element={<Navigate to=\"tickets\" replace />} />,\r\n <Route key=\"legacy-sla\" path=\"sla\" element={<Navigate to=\"admin/sla\" replace />} />,\r\n <Route key=\"legacy-templates\" path=\"templates\" element={<Navigate to=\"admin/templates\" replace />} />,\r\n <Route key=\"legacy-admin-esc\" path=\"admin/escalation\" element={<Navigate to=\"tickets/escalation\" replace />} />,\r\n ];\r\n default:\r\n return [];\r\n }\r\n}\r\n\r\n// ===========================================================================\r\n// Protected catch-all: access denied for authenticated users\r\n// ===========================================================================\r\n\r\n/**\r\n * Renders inside the protected route tree (after all dynamic routes).\r\n * When an authenticated user navigates to a URL that doesn't match any\r\n * dynamic route, this means they lack access to the requested app/module.\r\n * Shows AccessDenied with tenant info and a link to the applications list.\r\n */\r\nfunction ProtectedCatchAll() {\r\n const { isLoading, menu } = useNavigation();\r\n const { currentTenant } = useTenant();\r\n const { user } = useAuth();\r\n const location = useLocation();\r\n\r\n // Show loader while menu is still loading\r\n if (isLoading) return <PageLoader />;\r\n\r\n // Extract permission path from URL (strip tenant prefix)\r\n const parts = location.pathname.split('/').filter(Boolean);\r\n const appSegments = parts[0] === 't' ? parts.slice(2) : parts;\r\n const permissionPath = appSegments.slice(0, 2).join('.');\r\n const applicationsUrl = currentTenant?.slug\r\n ? `/t/${currentTenant.slug}/applications`\r\n : '/applications';\r\n\r\n // Determine actual denial reason:\r\n // 1. If app not in menu (tenant doesn't have it) → \"tenant\"\r\n // 2. If app in menu but user lacks permissions → \"permission\"\r\n // 3. If app in menu AND user has permissions → \"permission\" (route exists but page component\r\n // is not registered — this is more helpful than \"tenant\" which implies wrong workspace)\r\n const appCode = appSegments[0]?.toLowerCase();\r\n const appInMenu = appCode ? menu?.applications.some(app => app.code?.toLowerCase() === appCode) ?? false : false;\r\n const reason: 'tenant' | 'permission' = appInMenu ? 'permission' : 'tenant';\r\n\r\n return (\r\n <div className=\"min-h-screen bg-[var(--bg-primary)]\">\r\n <AppHeader onMenuToggle={() => {}} />\r\n <main className=\"pt-16 transition-all duration-300 h-[calc(100vh-4rem)]\">\r\n <div className=\"p-4 sm:p-6 lg:px-10 h-full overflow-auto\">\r\n <AccessDenied\r\n reason={reason}\r\n tenantName={currentTenant?.name}\r\n permissionPath={permissionPath || undefined}\r\n applicationsUrl={applicationsUrl}\r\n userRoles={user?.roles}\r\n userPermissions={user?.permissions}\r\n />\r\n </div>\r\n </main>\r\n </div>\r\n );\r\n}\r\n\r\n// ===========================================================================\r\n// Catch-all: loading-aware 404\r\n// ===========================================================================\r\n\r\n/**\r\n * Shows a loader while the navigation menu is still being fetched (e.g. right\r\n * after login). Once the menu has loaded, falls back to the real 404 page.\r\n */\r\nfunction CatchAllRoute() {\r\n const { isLoading } = useNavigation();\r\n const hasToken = !!localStorage.getItem('token');\r\n\r\n if (hasToken && isLoading) {\r\n return <PageLoader />;\r\n }\r\n\r\n return <NotFoundPage />;\r\n}\r\n\r\n// ===========================================================================\r\n// DynamicRouter\r\n// ===========================================================================\r\n\r\n/**\r\n * DynamicRouter — Generates React Router routes from the navigation API + PageRegistry.\r\n *\r\n * Architecture:\r\n * - Static routes (auth, docs, home, 404) are hardcoded and always available\r\n * - Dynamic routes come from the enriched /api/navigation/menu response\r\n * - Components are resolved via PageRegistry (supports lazy loading)\r\n * - Implicit routes (detail, create, edit) are auto-generated from conventions\r\n * - Module-level feature gating wraps modules with requiredFeature\r\n * - Auto-redirects are generated for modules/sections without index components\r\n * - Outlet-based tab sections (Settings, AppSettings) resolve tabs from PageRegistry\r\n * - Multi-tenant routes (/t/:slug/*) are handled via a single route tree (no duplication)\r\n *\r\n * This component replaces both the inline routes in App.tsx and smartstackRoutes.tsx,\r\n * eliminating the route duplication between tenant/standard paths.\r\n */\r\nexport function DynamicRouter(): ReactElement {\r\n const { menu, isLoading } = useNavigation();\r\n\r\n // Build dynamic application routes from the menu API\r\n const dynamicAppRoutes = useMemo(() => {\r\n if (!menu || isLoading) return [];\r\n\r\n return menu.applications.map(app => {\r\n const appRoute = app.route?.replace(/^\\//, '') || app.code;\r\n const appRoutes = buildApplicationRoutes(app);\r\n const wrappedRoutes = wrapWithFeatureGuard(app, appRoutes);\r\n const staticRoutes = getStaticAppRoutes(app.code);\r\n\r\n return (\r\n <Route key={app.code} path={appRoute} element={<AppLayout />}>\r\n {wrappedRoutes}\r\n {staticRoutes}\r\n </Route>\r\n );\r\n });\r\n }, [menu, isLoading]);\r\n\r\n // Shared protected routes (used for both /t/:slug/* and standard paths)\r\n const protectedRoutes = (\r\n <>\r\n <Route element={<ProtectedRoute />}>\r\n <Route element={<LicenseGuard />}>\r\n {/* Dynamic application routes from API */}\r\n {dynamicAppRoutes}\r\n\r\n {/* System routes — unconditional, not dependent on PageRegistry */}\r\n <Route path=\"applications\" element={<AppLayout />}>\r\n <Route index element={<ApplicationsPage />} />\r\n </Route>\r\n <Route path=\"notifications\" element={<AppLayout />}>\r\n <Route index element={<NotificationsPage />} />\r\n </Route>\r\n\r\n {/* Cross-app legacy redirects (inside protected area for tenant support) */}\r\n <Route path=\"administration/external-apps\" element={<TenantNavigate to=\"/api/accounts/list\" />} />\r\n <Route path=\"administration/external-apps/*\" element={<TenantNavigate to=\"/api/accounts/list\" />} />\r\n <Route path=\"api/external-apps\" element={<TenantNavigate to=\"/api/accounts/list\" />} />\r\n <Route path=\"api/external-apps/*\" element={<TenantNavigate to=\"/api/accounts/list\" />} />\r\n <Route path=\"support-client\" element={<TenantNavigate to=\"/support/my-tickets\" />} />\r\n <Route path=\"support-client/*\" element={<TenantNavigate to=\"/support/my-tickets\" />} />\r\n <Route path=\"user\" element={<TenantNavigate to=\"/myspace\" />} />\r\n <Route path=\"user/*\" element={<TenantNavigate to=\"/myspace\" />} />\r\n\r\n {/* Catch-all inside protected area — ensures ProtectedRoute handles\r\n access checks instead of falling to the top-level 404 */}\r\n <Route path=\"*\" element={<ProtectedCatchAll />} />\r\n </Route>\r\n </Route>\r\n </>\r\n );\r\n\r\n return (\r\n <Suspense fallback={<PageLoader />}>\r\n <PageTracker />\r\n <TenantTracker />\r\n <Routes>\r\n {/* Home */}\r\n <Route path=\"/\" element={<PublicLayout />}>\r\n <Route index element={<HomePage />} />\r\n </Route>\r\n\r\n {/* ================================================================ */}\r\n {/* Documentation — always accessible, not from DB navigation */}\r\n {/* ================================================================ */}\r\n {DocRoutes()}\r\n\r\n {/* ================================================================ */}\r\n {/* Auth routes */}\r\n {/* ================================================================ */}\r\n <Route path=\"/login\" element={<LoginPage />} />\r\n <Route path=\"/register\" element={<RegisterPage />} />\r\n <Route path=\"/confirm-email\" element={<ConfirmEmailPage />} />\r\n <Route path=\"/forgot-password\" element={<ForgotPasswordPage />} />\r\n <Route path=\"/reset-password\" element={<ResetPasswordPage />} />\r\n <Route path=\"/force-change-password\" element={<ForceChangePasswordPage />} />\r\n <Route path=\"/auth/callback\" element={<AuthCallbackPage />} />\r\n <Route path=\"/auth/onboarding\" element={<OnboardingWizardPage />} />\r\n\r\n {/* ================================================================ */}\r\n {/* Protected routes — tenant-prefixed and standard */}\r\n {/* ================================================================ */}\r\n\r\n {/* Tenant-prefixed routes (/t/:slug/*) */}\r\n <Route path=\"/t/:slug\" element={<TenantRouteWrapper />}>\r\n {protectedRoutes}\r\n </Route>\r\n\r\n {/* Standard routes (no tenant prefix) */}\r\n <Route element={<TenantRouteWrapper />}>\r\n {protectedRoutes}\r\n </Route>\r\n\r\n {/* ================================================================ */}\r\n {/* Top-level legacy redirects */}\r\n {/* ================================================================ */}\r\n <Route path=\"/admin\" element={<Navigate to=\"/administration\" replace />} />\r\n <Route path=\"/platform\" element={<Navigate to=\"/administration\" replace />} />\r\n <Route path=\"/platform/admin/*\" element={<Navigate to=\"/administration\" replace />} />\r\n <Route path=\"/platform/administration/*\" element={<Navigate to=\"/administration\" replace />} />\r\n <Route path=\"/platform/support/*\" element={<Navigate to=\"/support\" replace />} />\r\n <Route path=\"/dashboard\" element={<Navigate to=\"/myspace\" replace />} />\r\n <Route path=\"/personal\" element={<Navigate to=\"/myspace\" replace />} />\r\n <Route path=\"/personal/myspace\" element={<Navigate to=\"/myspace\" replace />} />\r\n <Route path=\"/personal/myspace/*\" element={<Navigate to=\"/myspace\" replace />} />\r\n <Route path=\"/user\" element={<Navigate to=\"/myspace\" replace />} />\r\n <Route path=\"/user/*\" element={<Navigate to=\"/myspace\" replace />} />\r\n <Route path=\"/business\" element={<Navigate to=\"/applications\" replace />} />\r\n <Route path=\"/business/support-client/*\" element={<Navigate to=\"/support/my-tickets\" replace />} />\r\n <Route path=\"/support-client/*\" element={<Navigate to=\"/support/my-tickets\" replace />} />\r\n <Route path=\"/support-client\" element={<Navigate to=\"/support/my-tickets\" replace />} />\r\n\r\n {/* 404 — show loader while menu is still being fetched after login */}\r\n <Route path=\"*\" element={<CatchAllRoute />} />\r\n </Routes>\r\n </Suspense>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport { X, Star, Check, Bug } from 'lucide-react';\r\nimport * as LucideIcons from 'lucide-react';\r\nimport type { LucideIcon } from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useNavigation, type ModuleDto, type ApplicationDto } from '../../contexts/NavigationContext';\r\nimport { useFavoriteModules, type FavoriteModule } from '../../hooks/useFavoriteModules';\r\n\r\ninterface FavoritesModalProps {\r\n readonly isOpen: boolean;\r\n readonly onClose: () => void;\r\n}\r\n\r\n// Dynamic icon component\r\nfunction DynamicIcon({ name, className }: { name: string; className?: string }) {\r\n const icons = LucideIcons as unknown as Record<string, LucideIcon>;\r\n const IconComponent = icons[name];\r\n if (!IconComponent) {\r\n return <Star className={className} />;\r\n }\r\n return <IconComponent className={className} />;\r\n}\r\n\r\nfunction getSelectionClass(isSelected: boolean, isDisabled: boolean): string {\r\n if (isSelected) return 'bg-[var(--color-accent-100)] border border-[var(--color-accent-500)]';\r\n if (isDisabled) return 'opacity-50 cursor-not-allowed bg-[var(--bg-secondary)]';\r\n return 'bg-[var(--bg-secondary)] hover:bg-[var(--bg-hover)] border border-transparent hover:border-[var(--border-color)]';\r\n}\r\n\r\nexport function FavoritesModal({ isOpen, onClose }: FavoritesModalProps): ReactElement | null {\r\n const { t } = useTranslation(['common', 'navigation']);\r\n const { menu } = useNavigation();\r\n const { favorites, toggleFavorite, isFavorite, canAddMore, maxFavorites } = useFavoriteModules();\r\n\r\n if (!isOpen) return null;\r\n\r\n // Gather all modules grouped by application\r\n const modulesByApp: { app: ApplicationDto; modules: ModuleDto[] }[] = [];\r\n\r\n menu?.applications.forEach(app => {\r\n if ((app.modules?.length ?? 0) > 0) {\r\n modulesByApp.push({\r\n app,\r\n modules: app.modules,\r\n });\r\n }\r\n });\r\n\r\n const handleToggle = (module: ModuleDto, app: ApplicationDto) => {\r\n // Resolve to first section's route if module has sections (module root may not have a page)\r\n const sortedSections = [...(module.sections || [])].sort((a, b) => a.displayOrder - b.displayOrder);\r\n const defaultRoute = sortedSections.length > 0 && sortedSections[0].route\r\n ? sortedSections[0].route\r\n : `/${app.code}/${module.code}`;\r\n\r\n const favoriteModule: FavoriteModule = {\r\n id: module.id,\r\n code: module.code,\r\n label: module.label,\r\n icon: module.icon || 'Star',\r\n route: defaultRoute,\r\n applicationCode: app.code,\r\n applicationLabel: app.label,\r\n };\r\n toggleFavorite(favoriteModule);\r\n };\r\n\r\n return (\r\n <div className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4\">\r\n <div className=\"bg-[var(--bg-primary)] rounded-[var(--radius-modal)] shadow-xl w-full max-w-lg max-h-[80vh] flex flex-col\">\r\n {/* Header */}\r\n <div className=\"flex items-center justify-between p-4 border-b border-[var(--border-color)]\">\r\n <div>\r\n <h2 className=\"text-lg font-semibold flex items-center gap-2\">\r\n <Star className=\"w-5 h-5 text-[var(--color-accent-500)]\" />\r\n {t('common:favorites.manageTitle')}\r\n </h2>\r\n <p className=\"text-sm text-[var(--text-secondary)] mt-1\">\r\n {t('common:favorites.selectModules', { count: favorites.length, max: maxFavorites })}\r\n </p>\r\n </div>\r\n <div className=\"flex items-center gap-1\">\r\n <button\r\n onClick={() => {\r\n const bugInfo = {\r\n component: 'FavoritesModal',\r\n url: window.location.href,\r\n };\r\n const issueUrl = `https://github.com/anthropics/claude-code/issues/new?title=${encodeURIComponent('[Bug] Favorites Modal')}&body=${encodeURIComponent(`## Context\\n- Component: ${bugInfo.component}\\n- URL: ${bugInfo.url}\\n\\n## Description\\n\\n<!-- Describe the bug here -->\\n\\n## Steps to Reproduce\\n\\n1. \\n2. \\n3. \\n\\n## Expected Behavior\\n\\n## Actual Behavior\\n`)}`;\r\n window.open(issueUrl, '_blank');\r\n }}\r\n className=\"p-2 rounded-[var(--radius-button)] hover:bg-[var(--bg-hover)] transition-colors text-[var(--text-secondary)]\"\r\n title={t('common:buttons.reportBug')}\r\n >\r\n <Bug className=\"w-5 h-5\" />\r\n </button>\r\n <button\r\n onClick={onClose}\r\n className=\"p-2 rounded-[var(--radius-button)] hover:bg-[var(--bg-hover)] transition-colors\"\r\n >\r\n <X className=\"w-5 h-5\" />\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\r\n {modulesByApp.map(({ app, modules }) => (\r\n <div key={app.id}>\r\n {/* Application header */}\r\n <div className=\"flex items-center gap-2 mb-2\">\r\n <DynamicIcon name={app.icon || 'Folder'} className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n <span className=\"text-sm font-medium text-[var(--text-secondary)]\">\r\n {app.label}\r\n </span>\r\n </div>\r\n\r\n {/* Modules list */}\r\n <div className=\"space-y-1 ml-6\">\r\n {modules.map((module) => {\r\n const isSelected = isFavorite(module.id);\r\n const isDisabled = !isSelected && !canAddMore;\r\n\r\n return (\r\n <button\r\n key={module.id}\r\n onClick={() => handleToggle(module, app)}\r\n disabled={isDisabled}\r\n className={`w-full flex items-center gap-3 p-2.5 rounded-[var(--radius-button)] transition-all ${\r\n getSelectionClass(isSelected, isDisabled)\r\n }`}\r\n >\r\n <div className={`w-8 h-8 flex items-center justify-center rounded-[var(--radius-button)] ${\r\n isSelected\r\n ? 'bg-[var(--color-accent-600)] text-white'\r\n : 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]'\r\n }`}>\r\n <DynamicIcon name={module.icon || 'Star'} className=\"w-4 h-4\" />\r\n </div>\r\n <span className={`flex-1 text-left text-sm ${\r\n isSelected ? 'font-medium text-[var(--color-accent-700)]' : 'text-[var(--text-primary)]'\r\n }`}>\r\n {module.label}\r\n </span>\r\n {isSelected && (\r\n <Check className=\"w-4 h-4 text-[var(--color-accent-600)]\" />\r\n )}\r\n </button>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n ))}\r\n\r\n {modulesByApp.length === 0 && (\r\n <div className=\"text-center py-8 text-[var(--text-secondary)]\">\r\n {t('common:favorites.noModules')}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Footer */}\r\n <div className=\"p-4 border-t border-[var(--border-color)]\">\r\n <button\r\n onClick={onClose}\r\n className=\"w-full px-4 py-2 bg-[var(--color-accent-600)] text-white rounded-[var(--radius-button)] hover:bg-[var(--color-accent-700)] transition-colors font-medium\"\r\n >\r\n {t('common:actions.done')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useNavigate } from 'react-router-dom';\r\nimport { Star, Plus, Settings2 } from 'lucide-react';\r\nimport * as LucideIcons from 'lucide-react';\r\nimport type { LucideIcon } from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { useFavoriteModules, type FavoriteModule } from '../../hooks/useFavoriteModules';\r\nimport { useFavorites } from '../../contexts/FavoritesContext';\r\nimport { useNavigation } from '../../contexts/NavigationContext';\r\nimport { FavoritesModal } from './FavoritesModal';\r\n\r\ninterface FavoritesBarProps {\r\n readonly className?: string;\r\n}\r\n\r\n// Dynamic icon component\r\nfunction DynamicIcon({ name, className }: { name: string; className?: string }) {\r\n const icons = LucideIcons as unknown as Record<string, LucideIcon>;\r\n const IconComponent = icons[name];\r\n if (!IconComponent) {\r\n return <Star className={className} />;\r\n }\r\n return <IconComponent className={className} />;\r\n}\r\n\r\nexport function FavoritesBar({ className = '' }: FavoritesBarProps): ReactElement {\r\n const { t } = useTranslation(['common']);\r\n const navigate = useNavigate();\r\n const { favorites, maxFavorites } = useFavoriteModules();\r\n const { triggerExpand } = useFavorites();\r\n const { menu } = useNavigation();\r\n const [isModalOpen, setIsModalOpen] = useState(false);\r\n\r\n const emptySlots = maxFavorites - favorites.length;\r\n\r\n // Resolve favorite route to first section if module has sections (handles legacy favorites)\r\n const resolveRoute = (module: FavoriteModule): string => {\r\n if (menu) {\r\n for (const app of menu.applications) {\r\n const navModule = app.modules.find(m => m.id === module.id);\r\n if (navModule?.sections?.length) {\r\n const firstSection = [...navModule.sections].sort((a, b) => a.displayOrder - b.displayOrder)[0];\r\n if (firstSection.route) return firstSection.route;\r\n }\r\n }\r\n }\r\n return module.route;\r\n };\r\n\r\n const handleModuleClick = (module: FavoriteModule) => {\r\n const route = resolveRoute(module);\r\n if (route) {\r\n // Trigger sidebar expansion before navigating\r\n triggerExpand();\r\n navigate(route);\r\n }\r\n };\r\n\r\n return (\r\n <>\r\n <div className={`bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)] p-4 ${className}`}>\r\n {/* Header */}\r\n <div className=\"flex items-center justify-between mb-3\">\r\n <div className=\"flex items-center gap-2\">\r\n <Star className=\"w-4 h-4 text-[var(--color-accent-500)]\" />\r\n <span className=\"text-sm font-medium text-[var(--text-primary)]\">\r\n {t('common:favorites.quickAccess')}\r\n </span>\r\n </div>\r\n <button\r\n onClick={() => setIsModalOpen(true)}\r\n className=\"flex items-center gap-1.5 px-2 py-1 text-xs font-medium text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] rounded-[var(--radius-button)] transition-colors\"\r\n >\r\n <Settings2 className=\"w-3.5 h-3.5\" />\r\n {t('common:favorites.manage')}\r\n </button>\r\n </div>\r\n\r\n {/* Favorites Grid */}\r\n <div className=\"flex gap-2 overflow-x-auto pb-1\">\r\n {/* Favorite modules */}\r\n {favorites.map((module) => (\r\n <button\r\n key={module.id}\r\n onClick={() => handleModuleClick(module)}\r\n className=\"flex flex-col items-center gap-1.5 min-w-[80px] p-3 bg-[var(--bg-secondary)] hover:bg-[var(--bg-hover)] border border-[var(--border-color)] hover:border-[var(--color-accent-500)] rounded-[var(--radius-card)] transition-all group\"\r\n title={`${module.applicationLabel} - ${module.label}`}\r\n >\r\n <div className=\"w-8 h-8 flex items-center justify-center rounded-[var(--radius-button)] bg-[var(--color-accent-100)] text-[var(--color-accent-600)] group-hover:bg-[var(--color-accent-600)] group-hover:text-white transition-colors\">\r\n <DynamicIcon name={module.icon || 'Star'} className=\"w-4 h-4\" />\r\n </div>\r\n <span className=\"text-xs font-medium text-[var(--text-secondary)] group-hover:text-[var(--text-primary)] truncate max-w-[70px]\">\r\n {module.label}\r\n </span>\r\n </button>\r\n ))}\r\n\r\n {/* Empty slots */}\r\n {Array.from({ length: emptySlots }).map((_, index) => (\r\n <button\r\n key={`empty-slot-${index}`}\r\n onClick={() => setIsModalOpen(true)}\r\n className=\"flex flex-col items-center justify-center gap-1.5 min-w-[80px] p-3 border-2 border-dashed border-[var(--border-color)] hover:border-[var(--color-accent-500)] rounded-[var(--radius-card)] transition-colors group\"\r\n >\r\n <div className=\"w-8 h-8 flex items-center justify-center rounded-[var(--radius-button)] bg-[var(--bg-tertiary)] text-[var(--text-muted)] group-hover:bg-[var(--color-accent-100)] group-hover:text-[var(--color-accent-600)] transition-colors\">\r\n <Plus className=\"w-4 h-4\" />\r\n </div>\r\n <span className=\"text-xs text-[var(--text-muted)] group-hover:text-[var(--text-secondary)]\">\r\n {t('common:favorites.add')}\r\n </span>\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Modal */}\r\n <FavoritesModal\r\n isOpen={isModalOpen}\r\n onClose={() => setIsModalOpen(false)}\r\n />\r\n </>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport {\r\n PlusCircle,\r\n Play,\r\n Pause,\r\n CheckCircle,\r\n XCircle,\r\n RefreshCcw,\r\n MessageSquare,\r\n UserPlus,\r\n AlertTriangle,\r\n TrendingUp,\r\n Edit,\r\n Trash2,\r\n Paperclip,\r\n ArrowRight,\r\n} from 'lucide-react';\r\n\r\nexport interface ActivityItem {\r\n id: string;\r\n ticketId: string;\r\n action: string;\r\n fieldChanged: string | null;\r\n oldValue: string | null;\r\n newValue: string | null;\r\n changedByUserId: string | null;\r\n changedByUserName: string | null;\r\n createdAt: string;\r\n}\r\n\r\ninterface ActivityTimelineProps {\r\n readonly activities: ActivityItem[];\r\n readonly loading?: boolean;\r\n}\r\n\r\nexport function ActivityTimeline({ activities, loading = false }: ActivityTimelineProps): ReactElement {\r\n if (loading) {\r\n return (\r\n <div className=\"animate-pulse space-y-4\">\r\n {[1, 2, 3].map((i) => (\r\n <div key={`skeleton-${i}`} className=\"flex gap-4\">\r\n <div className=\"w-8 h-8 bg-[var(--bg-secondary)] rounded-full\" />\r\n <div className=\"flex-1\">\r\n <div className=\"h-4 bg-[var(--bg-secondary)] rounded w-3/4 mb-2\" />\r\n <div className=\"h-3 bg-[var(--bg-secondary)] rounded w-1/4\" />\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n }\r\n\r\n if (activities.length === 0) {\r\n return (\r\n <div className=\"text-center py-8 text-[var(--text-secondary)]\">\r\n <RefreshCcw className=\"w-12 h-12 mx-auto mb-4 opacity-30\" />\r\n <p>No activity recorded yet</p>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"relative\">\r\n {/* Timeline line */}\r\n <div className=\"absolute left-4 top-0 bottom-0 w-0.5 bg-[var(--border-color)]\" />\r\n\r\n <div className=\"space-y-4\">\r\n {activities.map((activity, index) => (\r\n <ActivityEntry key={activity.id} activity={activity} isLast={index === activities.length - 1} />\r\n ))}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction ActivityEntry({ activity, isLast }: { activity: ActivityItem; isLast: boolean }) {\r\n const { icon, color, bgColor } = getActivityStyle(activity.action);\r\n const Icon = icon;\r\n\r\n return (\r\n <div className=\"relative flex gap-4 pl-0\">\r\n {/* Icon */}\r\n <div\r\n className={`relative z-10 w-8 h-8 rounded-full flex items-center justify-center ${bgColor}`}\r\n >\r\n <Icon className={`w-4 h-4 ${color}`} />\r\n </div>\r\n\r\n {/* Content */}\r\n <div className={`flex-1 pb-4 ${!isLast ? '' : ''}`}>\r\n <div className=\"flex items-start justify-between gap-2\">\r\n <div>\r\n <p className=\"text-sm\">\r\n {activity.changedByUserName ? (\r\n <span className=\"font-medium\">{activity.changedByUserName}</span>\r\n ) : (\r\n <span className=\"font-medium text-[var(--text-secondary)]\">System</span>\r\n )}{' '}\r\n <span className=\"text-[var(--text-secondary)]\">\r\n {getActivityDescription(activity)}\r\n </span>\r\n </p>\r\n\r\n {/* Field change details */}\r\n {activity.fieldChanged && activity.oldValue && activity.newValue && (\r\n <div className=\"mt-2 flex items-center gap-2 text-sm\">\r\n <span className=\"px-2 py-0.5 bg-[var(--error-bg)] text-[var(--error-text)] rounded line-through\">\r\n {activity.oldValue}\r\n </span>\r\n <ArrowRight className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n <span className=\"px-2 py-0.5 bg-[var(--success-bg)] text-[var(--success-text)] rounded\">\r\n {activity.newValue}\r\n </span>\r\n </div>\r\n )}\r\n </div>\r\n\r\n <span className=\"text-xs text-[var(--text-secondary)] whitespace-nowrap\">\r\n {formatTime(activity.createdAt)}\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nconst activityStyleMap: Record<\r\n string,\r\n { icon: typeof PlusCircle; color: string; bgColor: string }\r\n> = {\r\n Created: {\r\n icon: PlusCircle,\r\n color: 'text-[var(--success-text)]',\r\n bgColor: 'bg-[var(--success-bg)]',\r\n },\r\n StatusChanged: {\r\n icon: RefreshCcw,\r\n color: 'text-[var(--info-text)]',\r\n bgColor: 'bg-[var(--info-bg)]',\r\n },\r\n PriorityChanged: {\r\n icon: AlertTriangle,\r\n color: 'text-[var(--warning-text)]',\r\n bgColor: 'bg-[var(--warning-bg)]',\r\n },\r\n Assigned: {\r\n icon: UserPlus,\r\n color: 'text-[var(--accent-text)]',\r\n bgColor: 'bg-[var(--accent-bg)]',\r\n },\r\n Commented: {\r\n icon: MessageSquare,\r\n color: 'text-[var(--info-text)]',\r\n bgColor: 'bg-[var(--info-bg)]',\r\n },\r\n AttachmentAdded: {\r\n icon: Paperclip,\r\n color: 'text-[var(--text-secondary)]',\r\n bgColor: 'bg-[var(--bg-secondary)]',\r\n },\r\n Resolved: {\r\n icon: CheckCircle,\r\n color: 'text-[var(--success-text)]',\r\n bgColor: 'bg-[var(--success-bg)]',\r\n },\r\n Closed: {\r\n icon: XCircle,\r\n color: 'text-[var(--text-secondary)]',\r\n bgColor: 'bg-[var(--bg-secondary)]',\r\n },\r\n Reopened: {\r\n icon: RefreshCcw,\r\n color: 'text-[var(--warning-text)]',\r\n bgColor: 'bg-[var(--warning-bg)]',\r\n },\r\n SlaBreached: {\r\n icon: AlertTriangle,\r\n color: 'text-[var(--error-text)]',\r\n bgColor: 'bg-[var(--error-bg)]',\r\n },\r\n Escalated: {\r\n icon: TrendingUp,\r\n color: 'text-[var(--error-text)]',\r\n bgColor: 'bg-[var(--error-bg)]',\r\n },\r\n Updated: {\r\n icon: Edit,\r\n color: 'text-[var(--info-text)]',\r\n bgColor: 'bg-[var(--info-bg)]',\r\n },\r\n Deleted: {\r\n icon: Trash2,\r\n color: 'text-[var(--error-text)]',\r\n bgColor: 'bg-[var(--error-bg)]',\r\n },\r\n OnHold: {\r\n icon: Pause,\r\n color: 'text-[var(--warning-text)]',\r\n bgColor: 'bg-[var(--warning-bg)]',\r\n },\r\n InProgress: {\r\n icon: Play,\r\n color: 'text-[var(--info-text)]',\r\n bgColor: 'bg-[var(--info-bg)]',\r\n },\r\n};\r\n\r\nconst defaultActivityStyle = {\r\n icon: RefreshCcw,\r\n color: 'text-[var(--text-secondary)]',\r\n bgColor: 'bg-[var(--bg-secondary)]',\r\n};\r\n\r\nfunction getActivityStyle(action: string): {\r\n icon: typeof PlusCircle;\r\n color: string;\r\n bgColor: string;\r\n} {\r\n return activityStyleMap[action] || defaultActivityStyle;\r\n}\r\n\r\nconst getStatusChangedDescription = (activity: ActivityItem): string =>\r\n activity.oldValue && activity.newValue\r\n ? `changed status from ${activity.oldValue} to ${activity.newValue}`\r\n : 'changed the status';\r\n\r\nconst getPriorityChangedDescription = (activity: ActivityItem): string =>\r\n activity.oldValue && activity.newValue\r\n ? `changed priority from ${activity.oldValue} to ${activity.newValue}`\r\n : 'changed the priority';\r\n\r\nconst getAssignedDescription = (activity: ActivityItem): string =>\r\n activity.newValue ? `assigned to ${activity.newValue}` : 'unassigned the ticket';\r\n\r\nconst getCommentedDescription = (activity: ActivityItem): string =>\r\n activity.newValue === 'internal' ? 'added an internal note' : 'added a comment';\r\n\r\nconst getAttachmentAddedDescription = (activity: ActivityItem): string =>\r\n activity.newValue ? `added attachment: ${activity.newValue}` : 'added an attachment';\r\n\r\nconst getSlaBrachedDescription = (activity: ActivityItem): string =>\r\n activity.newValue === 'response' ? 'Response SLA was breached' : 'Resolution SLA was breached';\r\n\r\nconst getEscalatedDescription = (activity: ActivityItem): string =>\r\n activity.newValue ? `escalated to ${activity.newValue}` : 'escalated this ticket';\r\n\r\nconst getUpdatedDescription = (activity: ActivityItem): string =>\r\n activity.fieldChanged ? `updated ${activity.fieldChanged}` : 'updated the ticket';\r\n\r\nconst descriptionMap: Record<string, (activity: ActivityItem) => string> = {\r\n Created: () => 'created this ticket',\r\n StatusChanged: getStatusChangedDescription,\r\n PriorityChanged: getPriorityChangedDescription,\r\n Assigned: getAssignedDescription,\r\n Commented: getCommentedDescription,\r\n AttachmentAdded: getAttachmentAddedDescription,\r\n Resolved: () => 'resolved this ticket',\r\n Closed: () => 'closed this ticket',\r\n Reopened: () => 'reopened this ticket',\r\n SlaBreached: getSlaBrachedDescription,\r\n Escalated: getEscalatedDescription,\r\n Updated: getUpdatedDescription,\r\n OnHold: () => 'put this ticket on hold',\r\n InProgress: () => 'started working on this ticket',\r\n};\r\n\r\nfunction getActivityDescription(activity: ActivityItem): string {\r\n const descriptionFn = descriptionMap[activity.action];\r\n if (descriptionFn) {\r\n return descriptionFn(activity);\r\n }\r\n return activity.action.toLowerCase();\r\n}\r\n\r\nfunction formatTime(dateStr: string): string {\r\n const date = new Date(dateStr);\r\n const now = new Date();\r\n const diff = now.getTime() - date.getTime();\r\n const minutes = Math.floor(diff / 60000);\r\n const hours = Math.floor(minutes / 60);\r\n const days = Math.floor(hours / 24);\r\n\r\n if (minutes < 1) return 'Just now';\r\n if (minutes < 60) return `${minutes}m ago`;\r\n if (hours < 24) return `${hours}h ago`;\r\n if (days < 7) return `${days}d ago`;\r\n\r\n return date.toLocaleDateString(undefined, {\r\n month: 'short',\r\n day: 'numeric',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n });\r\n}\r\n\r\n// Compact timeline for list views\r\nexport function ActivityBadge({ activities }: { activities: ActivityItem[] }): ReactElement | null {\r\n const lastActivity = activities[0];\r\n if (!lastActivity) return null;\r\n\r\n const { icon, color, bgColor } = getActivityStyle(lastActivity.action);\r\n const Icon = icon;\r\n\r\n return (\r\n <div className=\"flex items-center gap-2 text-xs\">\r\n <span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded ${bgColor} ${color}`}>\r\n <Icon className=\"w-3 h-3\" />\r\n {lastActivity.action}\r\n </span>\r\n <span className=\"text-[var(--text-secondary)]\">{formatTime(lastActivity.createdAt)}</span>\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\nimport { Clock, AlertTriangle, CheckCircle, XCircle, Pause } from 'lucide-react';\r\n\r\ninterface SlaBadgeProps {\r\n readonly responseDueAt: string;\r\n readonly resolutionDueAt: string;\r\n readonly responseBreached: boolean;\r\n readonly resolutionBreached: boolean;\r\n readonly firstResponseAt?: string | null;\r\n readonly isPaused: boolean;\r\n readonly responseSlaPercentage?: number;\r\n readonly resolutionSlaPercentage?: number;\r\n readonly showDetails?: boolean;\r\n}\r\n\r\nexport function SlaBadge({\r\n responseDueAt,\r\n resolutionDueAt,\r\n responseBreached,\r\n resolutionBreached,\r\n firstResponseAt,\r\n isPaused,\r\n responseSlaPercentage = 0,\r\n resolutionSlaPercentage = 0,\r\n showDetails = false,\r\n}: SlaBadgeProps): ReactElement {\r\n const now = new Date();\r\n const responseDue = new Date(responseDueAt);\r\n const resolutionDue = new Date(resolutionDueAt);\r\n\r\n const responseTimeLeft = responseDue.getTime() - now.getTime();\r\n const resolutionTimeLeft = resolutionDue.getTime() - now.getTime();\r\n\r\n const hasResponded = !!firstResponseAt;\r\n\r\n // Determine overall status\r\n let status: 'breached' | 'warning' | 'ok' | 'paused' | 'completed' = 'ok';\r\n let statusColor = 'green';\r\n let StatusIcon = CheckCircle;\r\n\r\n if (isPaused) {\r\n status = 'paused';\r\n statusColor = 'gray';\r\n StatusIcon = Pause;\r\n } else if (responseBreached || resolutionBreached) {\r\n status = 'breached';\r\n statusColor = 'red';\r\n StatusIcon = XCircle;\r\n } else if (\r\n (!hasResponded && responseTimeLeft < 30 * 60 * 1000) || // 30 min warning for response\r\n resolutionTimeLeft < 60 * 60 * 1000 // 1 hour warning for resolution\r\n ) {\r\n status = 'warning';\r\n statusColor = 'yellow';\r\n StatusIcon = AlertTriangle;\r\n }\r\n\r\n const colorClasses: Record<string, string> = {\r\n green: 'bg-[var(--success-bg)] text-[var(--success-text)] border-[var(--success-border)]',\r\n yellow: 'bg-[var(--warning-bg)] text-[var(--warning-text)] border-[var(--warning-border)]',\r\n red: 'bg-[var(--error-bg)] text-[var(--error-text)] border-[var(--error-border)]',\r\n gray: 'bg-[var(--bg-secondary)] text-[var(--text-secondary)] border-[var(--border-color)]',\r\n };\r\n\r\n if (!showDetails) {\r\n // Simple badge view\r\n return (\r\n <span className={`inline-flex items-center gap-1 px-2 py-1 rounded text-xs font-medium border ${colorClasses[statusColor]}`}>\r\n <StatusIcon className=\"w-3 h-3\" />\r\n {status === 'paused' && 'SLA Paused'}\r\n {status === 'breached' && 'SLA Breached'}\r\n {status === 'warning' && 'SLA Warning'}\r\n {status === 'ok' && 'Within SLA'}\r\n </span>\r\n );\r\n }\r\n\r\n // Detailed view\r\n return (\r\n <div className={`p-3 rounded-lg border ${colorClasses[statusColor]}`}>\r\n <div className=\"flex items-center gap-2 mb-2\">\r\n <StatusIcon className=\"w-4 h-4\" />\r\n <span className=\"font-medium\">\r\n {status === 'paused' && 'SLA Paused'}\r\n {status === 'breached' && 'SLA Breached'}\r\n {status === 'warning' && 'SLA Warning'}\r\n {status === 'ok' && 'Within SLA'}\r\n </span>\r\n </div>\r\n\r\n <div className=\"space-y-2 text-sm\">\r\n {/* Response SLA */}\r\n <div>\r\n <div className=\"flex items-center justify-between mb-1\">\r\n <span className=\"opacity-80\">Response</span>\r\n {responseBreached && (\r\n <span className=\"text-[var(--error-text)] font-medium\">Breached</span>\r\n )}\r\n {!responseBreached && hasResponded && (\r\n <span className=\"text-[var(--success-text)] font-medium\">Completed</span>\r\n )}\r\n {!responseBreached && !hasResponded && (\r\n <span>{formatTimeRemaining(responseTimeLeft)}</span>\r\n )}\r\n </div>\r\n {!hasResponded && !responseBreached && (\r\n <SlaProgressBar percentage={responseSlaPercentage} />\r\n )}\r\n </div>\r\n\r\n {/* Resolution SLA */}\r\n <div>\r\n <div className=\"flex items-center justify-between mb-1\">\r\n <span className=\"opacity-80\">Resolution</span>\r\n {resolutionBreached ? (\r\n <span className=\"text-[var(--error-text)] font-medium\">Breached</span>\r\n ) : (\r\n <span>{formatTimeRemaining(resolutionTimeLeft)}</span>\r\n )}\r\n </div>\r\n {!resolutionBreached && (\r\n <SlaProgressBar percentage={resolutionSlaPercentage} />\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction SlaProgressBar({ percentage }: { percentage: number }) {\r\n const getColor = () => {\r\n if (percentage >= 90) return 'bg-[var(--error-text)]';\r\n if (percentage >= 75) return 'bg-[var(--warning-text)]';\r\n return 'bg-[var(--success-text)]';\r\n };\r\n\r\n return (\r\n <div className=\"h-1.5 bg-white/50 rounded-full overflow-hidden\">\r\n <div\r\n className={`h-full rounded-full transition-all duration-300 ${getColor()}`}\r\n style={{ width: `${Math.min(percentage, 100)}%` }}\r\n />\r\n </div>\r\n );\r\n}\r\n\r\nfunction formatTimeRemaining(ms: number): string {\r\n if (ms <= 0) return 'Overdue';\r\n\r\n const minutes = Math.floor(ms / (1000 * 60));\r\n const hours = Math.floor(minutes / 60);\r\n const days = Math.floor(hours / 24);\r\n\r\n if (days > 0) {\r\n const remainingHours = hours % 24;\r\n return remainingHours > 0 ? `${days}d ${remainingHours}h` : `${days}d`;\r\n }\r\n\r\n if (hours > 0) {\r\n const remainingMinutes = minutes % 60;\r\n return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;\r\n }\r\n\r\n return `${minutes}m`;\r\n}\r\n\r\n// Compact SLA indicator for list views\r\nexport function SlaIndicator({\r\n responseBreached,\r\n resolutionBreached,\r\n isPaused,\r\n responseSlaPercentage = 0,\r\n resolutionSlaPercentage = 0,\r\n}: {\r\n responseBreached: boolean;\r\n resolutionBreached: boolean;\r\n isPaused: boolean;\r\n responseSlaPercentage?: number;\r\n resolutionSlaPercentage?: number;\r\n}): ReactElement {\r\n if (isPaused) {\r\n return (\r\n <div className=\"flex items-center gap-1\" title=\"SLA Paused\">\r\n <Pause className=\"w-4 h-4 text-[var(--text-secondary)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n if (responseBreached || resolutionBreached) {\r\n return (\r\n <div className=\"flex items-center gap-1\" title=\"SLA Breached\">\r\n <XCircle className=\"w-4 h-4 text-[var(--error-text)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n const maxPercentage = Math.max(responseSlaPercentage, resolutionSlaPercentage);\r\n\r\n if (maxPercentage >= 75) {\r\n return (\r\n <div className=\"flex items-center gap-1\" title={`${Math.round(maxPercentage)}% of SLA used`}>\r\n <AlertTriangle className=\"w-4 h-4 text-[var(--warning-text)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"flex items-center gap-1\" title=\"Within SLA\">\r\n <Clock className=\"w-4 h-4 text-[var(--success-text)]\" />\r\n </div>\r\n );\r\n}\r\n","import { useState, useEffect, useCallback } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { X, Search, Loader2, FileText, Globe } from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { templatesApi, type ResponseTemplateDto } from '@/services/api/supportApi';\r\n\r\nconst categoryColors: Record<string, string> = {\r\n Greeting: 'bg-[var(--info-bg)] text-[var(--info-text)]',\r\n Acknowledgment: 'bg-[var(--success-bg)] text-[var(--success-text)]',\r\n RequestInfo: 'bg-[var(--warning-bg)] text-[var(--warning-text)]',\r\n InProgress: 'bg-[var(--info-bg)] text-[var(--info-text)]',\r\n Resolution: 'bg-[var(--accent-bg)] text-[var(--accent-text)]',\r\n Escalation: 'bg-[var(--warning-bg)] text-[var(--warning-text)]',\r\n OnHold: 'bg-[var(--bg-secondary)] text-[var(--text-secondary)]',\r\n Closure: 'bg-[var(--bg-secondary)] text-[var(--text-secondary)]',\r\n Rejection: 'bg-[var(--error-bg)] text-[var(--error-text)]',\r\n General: 'bg-[var(--bg-secondary)] text-[var(--text-secondary)]',\r\n};\r\n\r\nconst SUPPORTED_LANGUAGES = [\r\n { code: 'fr', label: 'Fran\\u00e7ais' },\r\n { code: 'en', label: 'English' },\r\n { code: 'de', label: 'Deutsch' },\r\n { code: 'it', label: 'Italiano' },\r\n];\r\n\r\ninterface TemplatePickerProps {\r\n readonly isOpen: boolean;\r\n readonly onClose: () => void;\r\n readonly onSelect: (content: string) => void;\r\n readonly ticketVariables: Record<string, string>;\r\n}\r\n\r\nexport function TemplatePicker({ isOpen, onClose, onSelect, ticketVariables }: Readonly<TemplatePickerProps>): ReactElement | null {\r\n const { t, i18n } = useTranslation('support');\r\n const [templates, setTemplates] = useState<ResponseTemplateDto[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [rendering, setRendering] = useState<string | null>(null);\r\n const [search, setSearch] = useState('');\r\n const [selectedCategory, setSelectedCategory] = useState<string>('');\r\n const [selectedLanguage, setSelectedLanguage] = useState<string>(i18n.language?.substring(0, 2) || 'fr');\r\n const [categories, setCategories] = useState<string[]>([]);\r\n\r\n const loadTemplates = useCallback((language: string) => {\r\n setLoading(true);\r\n Promise.all([\r\n templatesApi.getAll(undefined, true, language),\r\n templatesApi.getCategories(),\r\n ])\r\n .then(([tpls, cats]) => {\r\n setTemplates(tpls);\r\n setCategories(cats);\r\n })\r\n .catch(console.error)\r\n .finally(() => setLoading(false));\r\n }, []);\r\n\r\n useEffect(() => {\r\n if (!isOpen) return;\r\n setSelectedLanguage(i18n.language?.substring(0, 2) || 'fr');\r\n loadTemplates(i18n.language?.substring(0, 2) || 'fr');\r\n }, [isOpen, i18n.language, loadTemplates]);\r\n\r\n const handleLanguageChange = (lang: string) => {\r\n setSelectedLanguage(lang);\r\n loadTemplates(lang);\r\n };\r\n\r\n if (!isOpen) return null;\r\n\r\n const filtered = templates.filter((tpl) => {\r\n const matchesSearch = !search || tpl.title.toLowerCase().includes(search.toLowerCase()) || tpl.content.toLowerCase().includes(search.toLowerCase());\r\n const matchesCategory = !selectedCategory || tpl.category === selectedCategory;\r\n return matchesSearch && matchesCategory;\r\n });\r\n\r\n const handleSelect = async (tpl: ResponseTemplateDto) => {\r\n try {\r\n setRendering(tpl.id);\r\n const rendered = await templatesApi.render(tpl.id, ticketVariables);\r\n onSelect(rendered.content);\r\n onClose();\r\n } catch (error) {\r\n console.error('Failed to render template:', error);\r\n } finally {\r\n setRendering(null);\r\n }\r\n };\r\n\r\n return (\r\n <div className=\"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4\" onClick={onClose} onKeyDown={(e) => e.key === 'Escape' && onClose()} role=\"presentation\">\r\n <div className=\"bg-[var(--bg-card)] rounded-xl shadow-xl w-full max-w-lg max-h-[70vh] flex flex-col\" onClick={(e) => e.stopPropagation()} role=\"presentation\">\r\n {/* Header */}\r\n <div className=\"flex items-center justify-between p-4 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-2\">\r\n <FileText className=\"w-5 h-5 text-[var(--color-primary-500)]\" />\r\n <h3 className=\"font-semibold text-[var(--text-primary)]\">{t('templatePicker.title')}</h3>\r\n </div>\r\n <button onClick={onClose} className=\"p-1 rounded hover:bg-[var(--bg-secondary)]\">\r\n <X className=\"w-5 h-5 text-[var(--text-secondary)]\" />\r\n </button>\r\n </div>\r\n\r\n {/* Search + Filters */}\r\n <div className=\"p-3 space-y-2 border-b border-[var(--border-color)]\">\r\n {/* Language selector */}\r\n <div className=\"flex items-center gap-2\">\r\n <Globe className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n <div className=\"flex gap-1\">\r\n {SUPPORTED_LANGUAGES.map((lang) => (\r\n <button\r\n key={lang.code}\r\n onClick={() => handleLanguageChange(lang.code)}\r\n className={`px-2 py-1 text-xs rounded transition-colors ${selectedLanguage === lang.code ? 'bg-[var(--color-primary-500)] text-white' : 'bg-[var(--bg-secondary)] text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)]'}`}\r\n >\r\n {lang.label}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n <div className=\"relative\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-tertiary)]\" />\r\n <input\r\n type=\"text\"\r\n value={search}\r\n onChange={(e) => setSearch(e.target.value)}\r\n placeholder={t('templatePicker.searchPlaceholder')}\r\n className=\"w-full pl-9 pr-3 py-2 text-sm bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded-lg outline-none focus:ring-1 focus:ring-[var(--color-primary-500)]\"\r\n autoFocus\r\n />\r\n </div>\r\n <div className=\"flex gap-1 flex-wrap\">\r\n <button\r\n onClick={() => setSelectedCategory('')}\r\n className={`px-2 py-1 text-xs rounded-full transition-colors ${!selectedCategory ? 'bg-[var(--color-primary-500)] text-white' : 'bg-[var(--bg-secondary)] text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)]'}`}\r\n >\r\n {t('templatePicker.allCategories')}\r\n </button>\r\n {categories.map((cat) => (\r\n <button\r\n key={cat}\r\n onClick={() => setSelectedCategory(cat === selectedCategory ? '' : cat)}\r\n className={`px-2 py-1 text-xs rounded-full transition-colors ${cat === selectedCategory ? 'bg-[var(--color-primary-500)] text-white' : `${categoryColors[cat] || 'bg-[var(--bg-secondary)] text-[var(--text-secondary)]'} hover:opacity-80`}`}\r\n >\r\n {t(`templates.categories.${cat}`, { defaultValue: cat })}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Template List */}\r\n <div className=\"flex-1 overflow-y-auto p-2\">\r\n {loading && (\r\n <div className=\"flex items-center justify-center py-8\">\r\n <Loader2 className=\"w-6 h-6 animate-spin text-[var(--text-tertiary)]\" />\r\n </div>\r\n )}\r\n {!loading && filtered.length === 0 && (\r\n <p className=\"text-center py-8 text-sm text-[var(--text-tertiary)]\">{t('templatePicker.noResults')}</p>\r\n )}\r\n {!loading && filtered.length > 0 && (\r\n <div className=\"space-y-1\">\r\n {filtered.map((tpl) => (\r\n <button\r\n key={tpl.id}\r\n onClick={() => handleSelect(tpl)}\r\n disabled={rendering !== null}\r\n className=\"w-full text-left p-3 rounded-lg hover:bg-[var(--bg-secondary)] transition-colors group disabled:opacity-50\"\r\n >\r\n <div className=\"flex items-center gap-2 mb-1\">\r\n <span className={`px-2 py-0.5 text-[10px] rounded-full ${categoryColors[tpl.category] || 'bg-[var(--bg-secondary)]'}`}>\r\n {t(`templates.categories.${tpl.category}`, { defaultValue: tpl.category })}\r\n </span>\r\n <span className=\"font-medium text-sm text-[var(--text-primary)]\">{tpl.title}</span>\r\n {rendering === tpl.id && <Loader2 className=\"w-3 h-3 animate-spin ml-auto\" />}\r\n </div>\r\n <p className=\"text-xs text-[var(--text-tertiary)] line-clamp-2\">{tpl.content}</p>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Footer */}\r\n <div className=\"p-3 border-t border-[var(--border-color)]\">\r\n <p className=\"text-xs text-[var(--text-tertiary)] text-center\">{t('templatePicker.selectTemplate')}</p>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState, useRef, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport {\r\n Send, Loader2, MessageSquare, AlertTriangle,\r\n CheckCircle, XCircle, RotateCcw, UserPlus, Clock,\r\n ArrowRightLeft, Paperclip, X, Wifi, WifiOff,\r\n Eye, Download, FileText, Image as ImageIcon\r\n} from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { TemplatePicker } from './TemplatePicker';\r\nimport type { TicketActivityAction } from '@/services/api/ticketApi';\r\nimport { apiClient } from '@/services/api/apiClient';\r\n\r\ntype TFunction = ReturnType<typeof useTranslation>['t'];\r\n\r\ninterface Comment {\r\n readonly id: string;\r\n readonly userName: string;\r\n readonly content: string;\r\n readonly isInternal?: boolean;\r\n readonly isEdited: boolean;\r\n readonly createdAt: string;\r\n}\r\n\r\ninterface Attachment {\r\n readonly id: string;\r\n readonly fileName: string;\r\n readonly contentType?: string;\r\n readonly fileSize: number;\r\n readonly createdAt: string;\r\n}\r\n\r\ninterface Activity {\r\n readonly id: string;\r\n readonly action: TicketActivityAction;\r\n readonly oldValue: string | null;\r\n readonly newValue: string | null;\r\n readonly changedByUserName?: string | null;\r\n readonly createdAt: string;\r\n}\r\n\r\ninterface TicketConversationProps {\r\n readonly ticketId: string;\r\n readonly comments: Comment[];\r\n readonly activities: Activity[];\r\n readonly attachments: Attachment[];\r\n readonly isSupport: boolean;\r\n readonly isClosed: boolean;\r\n readonly isConnected: boolean;\r\n readonly currentUserName?: string;\r\n readonly ticketVariables?: Record<string, string>;\r\n readonly onAddComment: (content: string, isInternal: boolean) => Promise<void>;\r\n readonly onDownloadAttachment: (attachment: Attachment) => void;\r\n readonly getAttachmentUrl: (attachment: Attachment) => string;\r\n}\r\n\r\n// Activity icons and colors\r\nconst activityConfig: Record<TicketActivityAction, { icon: React.ElementType; color: string; labelKey: string }> = {\r\n Created: { icon: Clock, color: 'var(--color-primary-500)', labelKey: 'history.actions.Created' },\r\n StatusChanged: { icon: ArrowRightLeft, color: 'var(--info-text)', labelKey: 'history.actions.StatusChanged' },\r\n PriorityChanged: { icon: AlertTriangle, color: 'var(--warning-text)', labelKey: 'history.actions.PriorityChanged' },\r\n Assigned: { icon: UserPlus, color: 'var(--success-text)', labelKey: 'history.actions.Assigned' },\r\n Unassigned: { icon: UserPlus, color: 'var(--text-secondary)', labelKey: 'history.actions.Unassigned' },\r\n Commented: { icon: MessageSquare, color: 'var(--color-primary-500)', labelKey: 'history.actions.Commented' },\r\n InternalCommented: { icon: MessageSquare, color: 'var(--warning-text)', labelKey: 'history.actions.InternalCommented' },\r\n AttachmentAdded: { icon: Paperclip, color: 'var(--success-text)', labelKey: 'history.actions.AttachmentAdded' },\r\n AttachmentRemoved: { icon: Paperclip, color: 'var(--error-text)', labelKey: 'history.actions.AttachmentRemoved' },\r\n Resolved: { icon: CheckCircle, color: 'var(--success-text)', labelKey: 'history.actions.Resolved' },\r\n Closed: { icon: XCircle, color: 'var(--text-secondary)', labelKey: 'history.actions.Closed' },\r\n Reopened: { icon: RotateCcw, color: 'var(--warning-text)', labelKey: 'history.actions.Reopened' },\r\n Rejected: { icon: XCircle, color: 'var(--error-text)', labelKey: 'history.actions.Rejected' },\r\n SlaResponseBreached: { icon: AlertTriangle, color: 'var(--error-text)', labelKey: 'history.actions.SlaResponseBreached' },\r\n SlaResolutionBreached: { icon: AlertTriangle, color: 'var(--error-text)', labelKey: 'history.actions.SlaResolutionBreached' },\r\n Escalated: { icon: ArrowRightLeft, color: 'var(--warning-text)', labelKey: 'history.actions.Escalated' },\r\n TitleChanged: { icon: MessageSquare, color: 'var(--text-secondary)', labelKey: 'history.actions.TitleChanged' },\r\n DescriptionChanged: { icon: MessageSquare, color: 'var(--text-secondary)', labelKey: 'history.actions.DescriptionChanged' },\r\n TypeChanged: { icon: ArrowRightLeft, color: 'var(--text-secondary)', labelKey: 'history.actions.TypeChanged' },\r\n SatisfactionSubmitted: { icon: CheckCircle, color: 'var(--success-text)', labelKey: 'history.actions.SatisfactionSubmitted' },\r\n};\r\n\r\nconst formatRelativeTime = (dateString: string, t: TFunction, locale?: string): string => {\r\n const date = new Date(dateString);\r\n const now = new Date();\r\n const diffMs = now.getTime() - date.getTime();\r\n const diffMins = Math.floor(diffMs / 60000);\r\n const diffHours = Math.floor(diffMs / 3600000);\r\n const diffDays = Math.floor(diffMs / 86400000);\r\n\r\n if (diffMins < 1) return t('history.timeAgo.now');\r\n if (diffMins < 60) return t('history.timeAgo.minutes', { count: diffMins });\r\n if (diffHours < 24) return t('history.timeAgo.hours', { count: diffHours });\r\n if (diffDays < 7) return t('history.timeAgo.days', { count: diffDays });\r\n return date.toLocaleDateString(locale, { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });\r\n};\r\n\r\nconst formatFileSize = (bytes: number): string => {\r\n if (bytes < 1024) return `${bytes} B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\r\n};\r\n\r\nconst isImageFile = (contentType?: string): boolean => {\r\n return contentType?.startsWith('image/') || false;\r\n};\r\n\r\nconst getMessageBubbleClasses = (isInternal: boolean, isOwn: boolean): string => {\r\n if (isInternal) {\r\n return 'px-4 py-2.5 shadow-sm rounded-2xl bg-[var(--warning-bg)] border border-[var(--warning-border)]';\r\n }\r\n if (isOwn) {\r\n return 'px-4 py-2.5 shadow-sm rounded-2xl rounded-tr-md bg-[var(--color-accent-600)] text-white';\r\n }\r\n return 'px-4 py-2.5 shadow-sm rounded-2xl rounded-tl-md bg-[var(--bg-card)] border border-[var(--border-color)]';\r\n};\r\n\r\n// Combine comments and activities into a timeline\r\ntype TimelineItem =\r\n | { readonly type: 'comment'; readonly id: string; readonly timestamp: Date; readonly data: Comment }\r\n | { readonly type: 'activity'; readonly id: string; readonly timestamp: Date; readonly data: Activity };\r\n\r\nfunction buildTimeline(comments: Comment[], activities: Activity[], isSupport: boolean): TimelineItem[] {\r\n const items: TimelineItem[] = [];\r\n\r\n // Add comments (filter internal for client mode)\r\n const visibleComments = isSupport ? comments : comments.filter(c => !c.isInternal);\r\n visibleComments.forEach(comment => {\r\n items.push({\r\n type: 'comment',\r\n id: `comment-${comment.id}`,\r\n timestamp: new Date(comment.createdAt),\r\n data: comment,\r\n });\r\n });\r\n\r\n // Add activities (exclude comment activities as they're already shown as messages)\r\n const excludeActivities: TicketActivityAction[] = ['Commented', 'InternalCommented'];\r\n activities\r\n .filter(a => !excludeActivities.includes(a.action))\r\n .forEach(activity => {\r\n items.push({\r\n type: 'activity',\r\n id: `activity-${activity.id}`,\r\n timestamp: new Date(activity.createdAt),\r\n data: activity,\r\n });\r\n });\r\n\r\n // Sort by timestamp\r\n items.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\r\n\r\n return items;\r\n}\r\n\r\n// System event bubble component\r\nfunction SystemEvent({ activity, t, locale }: Readonly<{ activity: Activity; t: TFunction; locale?: string }>) {\r\n const config = activityConfig[activity.action] || activityConfig.Created;\r\n const Icon = config.icon;\r\n\r\n return (\r\n <div className=\"flex justify-center my-4\">\r\n <div className=\"inline-flex items-center gap-2 px-4 py-2 rounded-full bg-[var(--bg-secondary)] border border-[var(--border-color)] text-sm\">\r\n <Icon className=\"w-4 h-4\" style={{ color: config.color }} />\r\n <span className=\"text-[var(--text-secondary)]\">{t(config.labelKey)}</span>\r\n {activity.newValue && activity.action === 'StatusChanged' && (\r\n <span className=\"font-medium text-[var(--text-primary)]\">\r\n → {activity.newValue}\r\n </span>\r\n )}\r\n {activity.newValue && activity.action === 'Assigned' && (\r\n <span className=\"font-medium text-[var(--text-primary)]\">\r\n {activity.newValue}\r\n </span>\r\n )}\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n {formatRelativeTime(activity.createdAt, t, locale)}\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// Get initials from name\r\nconst getInitials = (name: string): string => {\r\n const parts = name.trim().split(/\\s+/);\r\n if (parts.length >= 2) {\r\n return ((parts[0]?.at(0) ?? '') + (parts.at(-1)?.at(0) ?? '')).toUpperCase();\r\n }\r\n return name.slice(0, 2).toUpperCase();\r\n};\r\n\r\n// Get a consistent color based on name\r\nconst getAvatarColor = (name: string): string => {\r\n const colors = [\r\n '#6366f1', // indigo\r\n '#8b5cf6', // violet\r\n '#ec4899', // pink\r\n '#f43f5e', // rose\r\n '#f97316', // orange\r\n '#eab308', // yellow\r\n '#22c55e', // green\r\n '#14b8a6', // teal\r\n '#06b6d4', // cyan\r\n '#3b82f6', // blue\r\n ];\r\n let hash = 0;\r\n for (let i = 0; i < name.length; i++) {\r\n hash = name.charCodeAt(i) + ((hash << 5) - hash);\r\n }\r\n return colors[Math.abs(hash) % colors.length];\r\n};\r\n\r\n// Component to load authenticated images as blob URLs\r\nfunction AuthenticatedImage({\r\n src,\r\n alt,\r\n className,\r\n onClick,\r\n}: Readonly<{\r\n src: string;\r\n alt: string;\r\n className?: string;\r\n onClick?: (e: React.MouseEvent) => void;\r\n}>) {\r\n const [blobUrl, setBlobUrl] = useState<string | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState(false);\r\n\r\n useEffect(() => {\r\n let isMounted = true;\r\n const controller = new AbortController();\r\n\r\n apiClient.get<Blob>(src, {\r\n responseType: 'blob',\r\n signal: controller.signal,\r\n })\r\n .then(response => {\r\n if (isMounted) {\r\n const url = URL.createObjectURL(response.data);\r\n setBlobUrl(url);\r\n setLoading(false);\r\n }\r\n })\r\n .catch(() => {\r\n if (isMounted) {\r\n setError(true);\r\n setLoading(false);\r\n }\r\n });\r\n\r\n return () => {\r\n isMounted = false;\r\n controller.abort();\r\n if (blobUrl) {\r\n URL.revokeObjectURL(blobUrl);\r\n }\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [src]);\r\n\r\n if (loading) {\r\n return (\r\n <button type=\"button\" className={`flex items-center justify-center bg-[var(--bg-secondary)] ${className}`} onClick={onClick}>\r\n <Loader2 className=\"w-6 h-6 animate-spin text-[var(--text-tertiary)]\" />\r\n </button>\r\n );\r\n }\r\n\r\n if (error || !blobUrl) {\r\n return (\r\n <button type=\"button\" className={`flex items-center justify-center bg-[var(--bg-secondary)] ${className}`} onClick={onClick}>\r\n <ImageIcon className=\"w-6 h-6 text-[var(--text-tertiary)]\" />\r\n </button>\r\n );\r\n }\r\n\r\n return (\r\n <img\r\n src={blobUrl}\r\n alt={alt}\r\n className={className}\r\n onClick={onClick}\r\n />\r\n );\r\n}\r\n\r\n// Message bubble component\r\nfunction MessageBubble({\r\n comment,\r\n isOwn,\r\n isSupport,\r\n attachments,\r\n getAttachmentUrl,\r\n onDownloadAttachment,\r\n t,\r\n locale,\r\n}: Readonly<{\r\n comment: Comment;\r\n isOwn: boolean;\r\n isSupport: boolean;\r\n attachments: Attachment[];\r\n getAttachmentUrl: (attachment: Attachment) => string;\r\n onDownloadAttachment: (attachment: Attachment) => void;\r\n t: TFunction;\r\n locale?: string;\r\n}>) {\r\n const [previewImage, setPreviewImage] = useState<Attachment | null>(null);\r\n\r\n // Find attachments that might belong to this comment (based on similar timestamps)\r\n const commentTime = new Date(comment.createdAt).getTime();\r\n const relatedAttachments = attachments.filter(a => {\r\n const attachTime = new Date(a.createdAt).getTime();\r\n return Math.abs(attachTime - commentTime) < 60000; // Within 1 minute\r\n });\r\n\r\n const imageAttachments = relatedAttachments.filter(a => isImageFile(a.contentType));\r\n const docAttachments = relatedAttachments.filter(a => !isImageFile(a.contentType));\r\n\r\n const initials = getInitials(comment.userName);\r\n const avatarColor = getAvatarColor(comment.userName);\r\n\r\n return (\r\n <>\r\n <div className={`flex gap-3 mb-4 ${isOwn ? 'flex-row-reverse' : 'flex-row'}`}>\r\n {/* Avatar */}\r\n <div\r\n className=\"w-9 h-9 rounded-full flex items-center justify-center flex-shrink-0 text-white text-sm font-semibold shadow-sm\"\r\n style={{ backgroundColor: avatarColor }}\r\n title={comment.userName}\r\n >\r\n {initials}\r\n </div>\r\n\r\n {/* Message content */}\r\n <div className={`flex flex-col max-w-[70%] ${isOwn ? 'items-end' : 'items-start'}`}>\r\n {/* User info */}\r\n <div className={`flex items-center gap-2 mb-1 ${isOwn ? 'flex-row-reverse' : 'flex-row'}`}>\r\n <span className=\"text-xs font-medium text-[var(--text-secondary)]\">\r\n {comment.userName}\r\n </span>\r\n {isSupport && comment.isInternal && (\r\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-[var(--warning-bg)] text-[var(--warning-text)] border border-[var(--warning-border)]\">\r\n {t('conversation.internalNoteLabel')}\r\n </span>\r\n )}\r\n {comment.isEdited && (\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">{t('conversation.edited')}</span>\r\n )}\r\n </div>\r\n\r\n {/* Message bubble */}\r\n <div\r\n className={getMessageBubbleClasses(comment.isInternal || false, isOwn)}\r\n >\r\n <p className=\"whitespace-pre-wrap text-sm leading-relaxed\">{comment.content}</p>\r\n\r\n {/* Inline images */}\r\n {imageAttachments.length > 0 && (\r\n <div className={`mt-3 grid gap-2 ${imageAttachments.length > 1 ? 'grid-cols-2' : 'grid-cols-1'}`}>\r\n {imageAttachments.map(img => (\r\n <button\r\n key={img.id}\r\n className=\"relative cursor-pointer rounded-lg overflow-hidden group\"\r\n onClick={() => setPreviewImage(img)}\r\n type=\"button\"\r\n >\r\n <AuthenticatedImage\r\n src={getAttachmentUrl(img)}\r\n alt={img.fileName}\r\n className=\"w-full h-32 object-cover\"\r\n />\r\n <div className=\"absolute inset-0 bg-black/0 group-hover:bg-black/30 transition-colors flex items-center justify-center\">\r\n <Eye className=\"w-6 h-6 text-white opacity-0 group-hover:opacity-100 transition-opacity\" />\r\n </div>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Document attachments */}\r\n {docAttachments.length > 0 && (\r\n <div className=\"mt-3 space-y-2\">\r\n {docAttachments.map(doc => (\r\n <button\r\n key={doc.id}\r\n onClick={() => onDownloadAttachment(doc)}\r\n className={`flex items-center gap-2 w-full p-2 rounded-lg transition-colors ${\r\n isOwn && !comment.isInternal\r\n ? 'bg-white/10 hover:bg-white/20'\r\n : 'bg-[var(--bg-secondary)] hover:bg-[var(--bg-tertiary)]'\r\n }`}\r\n >\r\n <FileText className=\"w-4 h-4 flex-shrink-0\" />\r\n <span className=\"text-xs truncate flex-1 text-left\">{doc.fileName}</span>\r\n <span className=\"text-xs opacity-70\">{formatFileSize(doc.fileSize)}</span>\r\n <Download className=\"w-3.5 h-3.5\" />\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Timestamp */}\r\n <div className=\"text-[10px] text-[var(--text-tertiary)] mt-1 px-1\">\r\n {formatRelativeTime(comment.createdAt, t, locale)}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Image preview modal */}\r\n {previewImage && (\r\n <div\r\n className=\"fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-50 p-4\"\r\n onClick={() => setPreviewImage(null)}\r\n onKeyDown={(e) => e.key === 'Escape' && setPreviewImage(null)}\r\n role=\"presentation\"\r\n >\r\n <div className=\"relative max-w-4xl max-h-[90vh] w-full\">\r\n <button\r\n onClick={() => setPreviewImage(null)}\r\n className=\"absolute -top-10 right-0 text-white hover:text-gray-300\"\r\n >\r\n <X className=\"w-6 h-6\" />\r\n </button>\r\n <AuthenticatedImage\r\n src={getAttachmentUrl(previewImage)}\r\n alt={previewImage?.fileName}\r\n className=\"max-w-full max-h-[80vh] mx-auto object-contain rounded-lg\"\r\n onClick={(e) => e.stopPropagation()}\r\n />\r\n <div className=\"mt-4 flex items-center justify-center gap-4\">\r\n <p className=\"text-white text-sm\">{previewImage.fileName}</p>\r\n <button\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n onDownloadAttachment(previewImage);\r\n }}\r\n className=\"btn btn-secondary\"\r\n >\r\n <Download className=\"w-4 h-4 mr-2\" />\r\n {t('conversation.download')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n )}\r\n </>\r\n );\r\n}\r\n\r\nconst getTextareaPlaceholder = (isInternalComment: boolean, isSupport: boolean, t: TFunction): string => {\r\n if (isInternalComment) return t('conversation.internalPlaceholder', 'Ajouter une note interne...');\r\n if (isSupport) return t('conversation.supportPlaceholder', 'Répondre au client...');\r\n return t('conversation.clientPlaceholder', 'Écrire un message...');\r\n};\r\n\r\nconst getInputAreaClasses = (isInternalComment: boolean): string => {\r\n return `flex items-end gap-2 rounded-xl border ${isInternalComment ? 'border-[var(--warning-border)] bg-[var(--warning-bg)]/30' : 'border-[var(--border-color)] bg-[var(--bg-secondary)]'} p-2`;\r\n};\r\n\r\nconst getSendButtonClasses = (hasContent: boolean, _sending: boolean): string => {\r\n return `p-2.5 rounded-lg transition-colors ${\r\n hasContent\r\n ? 'bg-[var(--color-primary-600)] text-white hover:bg-[var(--color-primary-700)]'\r\n : 'bg-[var(--bg-tertiary)] text-[var(--text-tertiary)]'\r\n } disabled:opacity-50`;\r\n};\r\n\r\ninterface ConversationHeaderProps {\r\n readonly isConnected: boolean;\r\n readonly commentCount: number;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction ConversationHeader({ isConnected, commentCount, t }: Readonly<ConversationHeaderProps>) {\r\n return (\r\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-[var(--border-color)] bg-[var(--bg-card)]\">\r\n <div className=\"flex items-center gap-2\">\r\n <MessageSquare className=\"w-5 h-5 text-[var(--color-accent-600)]\" />\r\n <h2 className=\"font-semibold\">{t('conversation.title', 'Conversation')}</h2>\r\n <span className=\"text-sm text-[var(--text-secondary)]\">({commentCount})</span>\r\n </div>\r\n <div className=\"flex items-center gap-1.5\" title={isConnected ? t('conversation.realtimeActive') : t('conversation.realtimeInactive')}>\r\n {isConnected ? (\r\n <Wifi className=\"w-4 h-4 text-[var(--success-text)]\" />\r\n ) : (\r\n <WifiOff className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n )}\r\n <span className={`text-xs ${isConnected ? 'text-[var(--success-text)]' : 'text-[var(--text-tertiary)]'}`}>\r\n {isConnected ? t('conversation.live') : t('conversation.offline')}\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\ninterface EmptyStateProps {\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction EmptyState({ t }: Readonly<EmptyStateProps>) {\r\n return (\r\n <div className=\"flex flex-col items-center justify-center h-full text-[var(--text-secondary)]\">\r\n <MessageSquare className=\"w-12 h-12 mb-3 opacity-50\" />\r\n <p>{t('conversation.empty', 'Aucun message pour le moment')}</p>\r\n <p className=\"text-sm mt-1\">{t('conversation.startHint', 'Commencez la conversation ci-dessous')}</p>\r\n </div>\r\n );\r\n}\r\n\r\ninterface TimelineViewProps {\r\n readonly timeline: TimelineItem[];\r\n readonly currentUserName?: string;\r\n readonly isSupport: boolean;\r\n readonly attachments: Attachment[];\r\n readonly getAttachmentUrl: (attachment: Attachment) => string;\r\n readonly onDownloadAttachment: (attachment: Attachment) => void;\r\n readonly messagesEndRef: React.RefObject<HTMLDivElement | null>;\r\n readonly t: TFunction;\r\n readonly locale?: string;\r\n}\r\n\r\nfunction TimelineView({\r\n timeline,\r\n currentUserName,\r\n isSupport,\r\n attachments,\r\n getAttachmentUrl,\r\n onDownloadAttachment,\r\n messagesEndRef,\r\n t,\r\n locale,\r\n}: Readonly<TimelineViewProps>) {\r\n return (\r\n <>\r\n {timeline.map((item) => {\r\n if (item.type === 'activity') {\r\n return <SystemEvent key={item.id} activity={item.data} t={t} locale={locale} />;\r\n }\r\n const comment = item.data;\r\n const isOwn = comment.userName === currentUserName;\r\n return (\r\n <MessageBubble\r\n key={item.id}\r\n comment={comment}\r\n isOwn={isOwn}\r\n isSupport={isSupport}\r\n attachments={attachments}\r\n getAttachmentUrl={getAttachmentUrl}\r\n onDownloadAttachment={onDownloadAttachment}\r\n t={t}\r\n locale={locale}\r\n />\r\n );\r\n })}\r\n <div ref={messagesEndRef} />\r\n </>\r\n );\r\n}\r\n\r\ninterface InternalNoteToggleProps {\r\n readonly isInternalComment: boolean;\r\n readonly onToggle: (value: boolean) => void;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction InternalNoteToggle({ isInternalComment, onToggle, t }: Readonly<InternalNoteToggleProps>) {\r\n return (\r\n <div className=\"mb-2\">\r\n <label className=\"inline-flex items-center gap-2 cursor-pointer\">\r\n <input\r\n type=\"checkbox\"\r\n checked={isInternalComment}\r\n onChange={(e) => onToggle(e.target.checked)}\r\n className=\"rounded border-[var(--border-color)]\"\r\n />\r\n <span\r\n className={`text-sm ${isInternalComment ? 'text-[var(--warning-text)] font-medium' : 'text-[var(--text-secondary)]'}`}\r\n >\r\n {t('conversation.internalNote', 'Note interne (invisible pour le client)')}\r\n </span>\r\n </label>\r\n </div>\r\n );\r\n}\r\n\r\ninterface InputAreaProps {\r\n readonly newComment: string;\r\n readonly onCommentChange: (value: string) => void;\r\n readonly onKeyDown: (e: React.KeyboardEvent) => void;\r\n readonly onSend: () => void;\r\n readonly isInternalComment: boolean;\r\n readonly onInternalToggle: (value: boolean) => void;\r\n readonly sending: boolean;\r\n readonly isSupport: boolean;\r\n readonly textareaRef: React.RefObject<HTMLTextAreaElement | null>;\r\n readonly ticketVariables?: Record<string, string>;\r\n readonly onOpenTemplatePicker?: () => void;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction InputArea({\r\n newComment,\r\n onCommentChange,\r\n onKeyDown,\r\n onSend,\r\n isInternalComment,\r\n onInternalToggle,\r\n sending,\r\n isSupport,\r\n textareaRef,\r\n ticketVariables,\r\n onOpenTemplatePicker,\r\n t,\r\n}: Readonly<InputAreaProps>) {\r\n const hasContent = newComment.trim();\r\n\r\n return (\r\n <div className=\"border-t border-[var(--border-color)] bg-[var(--bg-card)] p-3\">\r\n {isSupport && <InternalNoteToggle isInternalComment={isInternalComment} onToggle={onInternalToggle} t={t} />}\r\n <div className={getInputAreaClasses(isInternalComment)}>\r\n <textarea\r\n ref={textareaRef}\r\n value={newComment}\r\n onChange={(e) => onCommentChange(e.target.value)}\r\n onKeyDown={onKeyDown}\r\n placeholder={getTextareaPlaceholder(isInternalComment, isSupport, t)}\r\n className=\"flex-1 bg-transparent border-none outline-none resize-none text-sm min-h-[24px] max-h-[150px] py-1 px-2\"\r\n rows={1}\r\n />\r\n <div className=\"flex items-center gap-1 flex-shrink-0\">\r\n {isSupport && ticketVariables && onOpenTemplatePicker && (\r\n <button\r\n onClick={onOpenTemplatePicker}\r\n className=\"p-2 rounded-lg text-[var(--text-tertiary)] hover:text-[var(--color-primary-500)] hover:bg-[var(--bg-tertiary)] transition-colors\"\r\n title={t('templatePicker.buttonTitle')}\r\n >\r\n <FileText className=\"w-4 h-4\" />\r\n </button>\r\n )}\r\n <button onClick={onSend} disabled={!hasContent || sending} className={getSendButtonClasses(!!hasContent, sending)}>\r\n {sending ? <Loader2 className=\"w-5 h-5 animate-spin\" /> : <Send className=\"w-5 h-5\" />}\r\n </button>\r\n </div>\r\n </div>\r\n <p className=\"text-xs text-[var(--text-tertiary)] mt-2 text-center\">\r\n {t('conversation.hint', 'Appuyez sur Entrée pour envoyer, Maj+Entrée pour un saut de ligne')}\r\n </p>\r\n </div>\r\n );\r\n}\r\n\r\ninterface ClosedStateProps {\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction ClosedState({ t }: Readonly<ClosedStateProps>) {\r\n return (\r\n <div className=\"border-t border-[var(--border-color)] bg-[var(--bg-secondary)] p-4 text-center\">\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {t('conversation.closed', 'Ce ticket est fermé. Aucun nouveau message ne peut être ajouté.')}\r\n </p>\r\n </div>\r\n );\r\n}\r\n\r\nexport function TicketConversation({\r\n comments,\r\n activities,\r\n attachments,\r\n isSupport,\r\n isClosed,\r\n isConnected,\r\n currentUserName,\r\n ticketVariables,\r\n onAddComment,\r\n onDownloadAttachment,\r\n getAttachmentUrl,\r\n}: TicketConversationProps): ReactElement {\r\n const { t, i18n } = useTranslation('support');\r\n const [newComment, setNewComment] = useState('');\r\n const [isInternalComment, setIsInternalComment] = useState(false);\r\n const [sending, setSending] = useState(false);\r\n const [showTemplatePicker, setShowTemplatePicker] = useState(false);\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const textareaRef = useRef<HTMLTextAreaElement>(null);\r\n\r\n const timeline = buildTimeline(comments, activities, isSupport);\r\n const visibleCommentCount = comments.filter((c) => isSupport || !c.isInternal).length;\r\n\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\r\n }, [timeline.length]);\r\n\r\n useEffect(() => {\r\n if (textareaRef.current) {\r\n textareaRef.current.style.height = 'auto';\r\n textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 150)}px`;\r\n }\r\n }, [newComment]);\r\n\r\n const handleSend = async () => {\r\n if (!newComment.trim() || sending) return;\r\n try {\r\n setSending(true);\r\n await onAddComment(newComment, isInternalComment);\r\n setNewComment('');\r\n setIsInternalComment(false);\r\n } finally {\r\n setSending(false);\r\n }\r\n };\r\n\r\n const handleKeyDown = (e: React.KeyboardEvent) => {\r\n if (e.key === 'Enter' && !e.shiftKey) {\r\n e.preventDefault();\r\n handleSend();\r\n }\r\n };\r\n\r\n const handleTemplateSelect = (content: string) => {\r\n setNewComment(prev => prev ? prev + '\\n' + content : content);\r\n };\r\n\r\n return (\r\n <div className=\"flex flex-col h-full min-h-0\">\r\n <ConversationHeader isConnected={isConnected} commentCount={visibleCommentCount} t={t} />\r\n\r\n <div className=\"flex-1 min-h-0 overflow-y-auto p-4 bg-[var(--bg-primary)]\">\r\n {timeline.length === 0 ? (\r\n <EmptyState t={t} />\r\n ) : (\r\n <TimelineView\r\n timeline={timeline}\r\n currentUserName={currentUserName}\r\n isSupport={isSupport}\r\n attachments={attachments}\r\n getAttachmentUrl={getAttachmentUrl}\r\n onDownloadAttachment={onDownloadAttachment}\r\n messagesEndRef={messagesEndRef}\r\n t={t}\r\n locale={i18n.language}\r\n />\r\n )}\r\n </div>\r\n\r\n {!isClosed ? (\r\n <InputArea\r\n newComment={newComment}\r\n onCommentChange={setNewComment}\r\n onKeyDown={handleKeyDown}\r\n onSend={handleSend}\r\n isInternalComment={isInternalComment}\r\n onInternalToggle={setIsInternalComment}\r\n sending={sending}\r\n isSupport={isSupport}\r\n textareaRef={textareaRef}\r\n ticketVariables={ticketVariables}\r\n onOpenTemplatePicker={isSupport && ticketVariables ? () => setShowTemplatePicker(true) : undefined}\r\n t={t}\r\n />\r\n ) : (\r\n <ClosedState t={t} />\r\n )}\r\n\r\n {ticketVariables && (\r\n <TemplatePicker\r\n isOpen={showTemplatePicker}\r\n onClose={() => setShowTemplatePicker(false)}\r\n onSelect={handleTemplateSelect}\r\n ticketVariables={ticketVariables}\r\n />\r\n )}\r\n </div>\r\n );\r\n}\r\n","import { useState, useEffect, useCallback, useRef } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { createPortal } from 'react-dom';\r\nimport {\r\n X, Download, ZoomIn, ZoomOut, RotateCw, FileText, File,\r\n Loader2, AlertTriangle, Maximize2, Minimize2\r\n} from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { apiClient } from '@/services/api/apiClient';\r\n\r\ninterface Attachment {\r\n id: string;\r\n fileName: string;\r\n contentType?: string;\r\n fileSize: number;\r\n createdAt: string;\r\n}\r\n\r\ninterface FilePreviewModalProps {\r\n readonly attachment: Attachment | null;\r\n readonly getUrl: (attachment: Attachment) => string;\r\n readonly onClose: () => void;\r\n readonly onDownload: (attachment: Attachment) => void;\r\n}\r\n\r\ntype FileType = 'image' | 'pdf' | 'text' | 'video' | 'audio' | 'unknown';\r\n\r\nconst getFileType = (attachment: Attachment): FileType => {\r\n const contentType = attachment.contentType?.toLowerCase() || '';\r\n const fileName = attachment.fileName.toLowerCase();\r\n const ext = fileName.split('.').pop() || '';\r\n\r\n // Check by contentType first\r\n if (contentType.startsWith('image/')) return 'image';\r\n if (contentType === 'application/pdf') return 'pdf';\r\n if (contentType.startsWith('text/') || contentType === 'application/json') return 'text';\r\n if (contentType.startsWith('video/')) return 'video';\r\n if (contentType.startsWith('audio/')) return 'audio';\r\n\r\n // Fallback to extension\r\n const imageExts = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp', 'ico'];\r\n const textExts = ['txt', 'csv', 'json', 'md', 'xml', 'log', 'html', 'css', 'js', 'ts'];\r\n const videoExts = ['mp4', 'webm', 'ogg', 'mov', 'avi'];\r\n const audioExts = ['mp3', 'wav', 'ogg', 'aac', 'm4a'];\r\n\r\n if (imageExts.includes(ext)) return 'image';\r\n if (ext === 'pdf') return 'pdf';\r\n if (textExts.includes(ext)) return 'text';\r\n if (videoExts.includes(ext)) return 'video';\r\n if (audioExts.includes(ext)) return 'audio';\r\n\r\n return 'unknown';\r\n};\r\n\r\nconst formatFileSize = (bytes: number): string => {\r\n if (bytes < 1024) return `${bytes} B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\r\n};\r\n\r\nexport function FilePreviewModal({\r\n attachment,\r\n getUrl,\r\n onClose,\r\n onDownload,\r\n}: FilePreviewModalProps): ReactElement | null {\r\n const { t } = useTranslation('support');\r\n const [zoom, setZoom] = useState(1);\r\n const [rotation, setRotation] = useState(0);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState<string | null>(null);\r\n const [textContent, setTextContent] = useState<string | null>(null);\r\n const [isFullscreen, setIsFullscreen] = useState(false);\r\n const [blobUrl, setBlobUrl] = useState<string | null>(null);\r\n const prevAttachmentRef = useRef<Attachment | null>(null);\r\n\r\n const fileType = attachment ? getFileType(attachment) : 'unknown';\r\n\r\n // Reset and load content when attachment changes\r\n useEffect(() => {\r\n // Skip if attachment hasn't changed\r\n if (prevAttachmentRef.current?.id === attachment?.id) return;\r\n prevAttachmentRef.current = attachment;\r\n\r\n // Revoke previous blob URL if exists\r\n if (blobUrl) {\r\n URL.revokeObjectURL(blobUrl);\r\n }\r\n\r\n // Reset all state for new attachment\r\n setZoom(1);\r\n setRotation(0);\r\n setLoading(true);\r\n setError(null);\r\n setTextContent(null);\r\n setBlobUrl(null);\r\n setIsFullscreen(false);\r\n\r\n if (!attachment) return;\r\n\r\n const currentFileType = getFileType(attachment);\r\n const currentUrl = getUrl(attachment);\r\n const abortController = new AbortController();\r\n\r\n // Load content based on file type using apiClient (handles baseURL + auth automatically)\r\n if (currentFileType === 'text') {\r\n // Text files: fetch and display content\r\n apiClient.get<string>(currentUrl, {\r\n signal: abortController.signal,\r\n responseType: 'text',\r\n })\r\n .then(response => {\r\n setTextContent(response.data);\r\n setLoading(false);\r\n })\r\n .catch((err) => {\r\n if (err.name === 'CanceledError' || err.code === 'ERR_CANCELED') return;\r\n setError(t('filePreview.loadError', 'Impossible de charger le fichier'));\r\n setLoading(false);\r\n });\r\n } else if (currentFileType === 'image' || currentFileType === 'video' || currentFileType === 'audio') {\r\n // Media files: fetch with auth and create blob URL\r\n apiClient.get<Blob>(currentUrl, {\r\n signal: abortController.signal,\r\n responseType: 'blob',\r\n })\r\n .then(response => {\r\n const objectUrl = URL.createObjectURL(response.data);\r\n setBlobUrl(objectUrl);\r\n setLoading(false);\r\n })\r\n .catch((err) => {\r\n if (err.name === 'CanceledError' || err.code === 'ERR_CANCELED') return;\r\n setError(t('filePreview.loadError', 'Impossible de charger le fichier'));\r\n setLoading(false);\r\n });\r\n } else if (currentFileType === 'pdf') {\r\n // PDF: fetch as blob and create blob URL (needed for auth)\r\n apiClient.get<Blob>(currentUrl, {\r\n signal: abortController.signal,\r\n responseType: 'blob',\r\n })\r\n .then(response => {\r\n const objectUrl = URL.createObjectURL(response.data);\r\n setBlobUrl(objectUrl);\r\n setLoading(false);\r\n })\r\n .catch((err) => {\r\n if (err.name === 'CanceledError' || err.code === 'ERR_CANCELED') return;\r\n setError(t('filePreview.loadError', 'Impossible de charger le fichier'));\r\n setLoading(false);\r\n });\r\n } else {\r\n // Unknown type: just show download option\r\n setLoading(false);\r\n }\r\n\r\n return () => {\r\n abortController.abort();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps -- blobUrl intentionally excluded to avoid infinite loop\r\n }, [attachment, getUrl, t]);\r\n\r\n // Cleanup blob URL on unmount\r\n useEffect(() => {\r\n return () => {\r\n if (blobUrl) {\r\n URL.revokeObjectURL(blobUrl);\r\n }\r\n };\r\n }, [blobUrl]);\r\n\r\n // Handle keyboard shortcuts\r\n useEffect(() => {\r\n if (!attachment) return;\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') {\r\n if (isFullscreen) {\r\n setIsFullscreen(false);\r\n } else {\r\n onClose();\r\n }\r\n }\r\n if (e.key === '+' || e.key === '=') {\r\n e.preventDefault();\r\n setZoom(z => Math.min(z + 0.25, 3));\r\n }\r\n if (e.key === '-') {\r\n e.preventDefault();\r\n setZoom(z => Math.max(z - 0.25, 0.25));\r\n }\r\n if (e.key === 'r' && !e.ctrlKey && !e.metaKey) {\r\n e.preventDefault();\r\n setRotation(r => (r + 90) % 360);\r\n }\r\n if (e.key === 'f') {\r\n e.preventDefault();\r\n setIsFullscreen(f => !f);\r\n }\r\n };\r\n\r\n window.addEventListener('keydown', handleKeyDown);\r\n return () => window.removeEventListener('keydown', handleKeyDown);\r\n }, [attachment, isFullscreen, onClose]);\r\n\r\n const handleZoomIn = useCallback(() => setZoom(z => Math.min(z + 0.25, 3)), []);\r\n const handleZoomOut = useCallback(() => setZoom(z => Math.max(z - 0.25, 0.25)), []);\r\n const handleRotate = useCallback(() => setRotation(r => (r + 90) % 360), []);\r\n\r\n if (!attachment) return null;\r\n\r\n const renderContent = () => {\r\n // Show loading spinner for non-media types (text, unknown)\r\n // Images/video/audio have their own loading overlay on top of the element\r\n if (loading && fileType !== 'pdf' && fileType !== 'image' && fileType !== 'video' && fileType !== 'audio') {\r\n return (\r\n <div className=\"flex items-center justify-center h-full\">\r\n <Loader2 className=\"w-8 h-8 animate-spin text-[var(--color-accent-500)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n if (error) {\r\n return (\r\n <div className=\"flex flex-col items-center justify-center h-full gap-4 text-center px-8\">\r\n <AlertTriangle className=\"w-12 h-12 text-[var(--warning-text)]\" />\r\n <p className=\"text-[var(--text-secondary)]\">{error}</p>\r\n <button\r\n onClick={() => onDownload(attachment)}\r\n className=\"btn btn-primary\"\r\n >\r\n <Download className=\"w-4 h-4 mr-2\" />\r\n {t('filePreview.download', 'Télécharger le fichier')}\r\n </button>\r\n </div>\r\n );\r\n }\r\n\r\n switch (fileType) {\r\n case 'image':\r\n return (\r\n <div className=\"relative flex items-center justify-center h-full overflow-auto p-4\">\r\n {!blobUrl ? (\r\n <div className=\"flex items-center justify-center\">\r\n <Loader2 className=\"w-8 h-8 animate-spin text-white\" />\r\n </div>\r\n ) : (\r\n <img\r\n src={blobUrl}\r\n alt={attachment.fileName}\r\n className=\"max-w-none transition-transform duration-200\"\r\n style={{\r\n transform: `scale(${zoom}) rotate(${rotation}deg)`,\r\n maxHeight: isFullscreen ? '90vh' : '70vh',\r\n }}\r\n onClick={(e) => e.stopPropagation()}\r\n />\r\n )}\r\n </div>\r\n );\r\n\r\n case 'pdf':\r\n return (\r\n <div className=\"h-full w-full\">\r\n {!blobUrl ? (\r\n <div className=\"flex items-center justify-center h-full\">\r\n <Loader2 className=\"w-8 h-8 animate-spin text-white\" />\r\n </div>\r\n ) : (\r\n <iframe\r\n src={`${blobUrl}#toolbar=1&navpanes=0`}\r\n className=\"w-full h-full border-0 rounded\"\r\n title={attachment.fileName}\r\n />\r\n )}\r\n </div>\r\n );\r\n\r\n case 'text':\r\n return (\r\n <div className=\"h-full overflow-auto p-4\">\r\n <pre className=\"text-sm text-[var(--text-primary)] font-mono whitespace-pre-wrap break-words bg-[var(--bg-secondary)] p-4 rounded-lg\">\r\n {textContent}\r\n </pre>\r\n </div>\r\n );\r\n\r\n case 'video':\r\n return (\r\n <div className=\"flex items-center justify-center h-full p-4\">\r\n {!blobUrl ? (\r\n <div className=\"flex items-center justify-center\">\r\n <Loader2 className=\"w-8 h-8 animate-spin text-white\" />\r\n </div>\r\n ) : (\r\n <video\r\n src={blobUrl}\r\n controls\r\n className=\"max-w-full max-h-[70vh] rounded-lg\"\r\n >\r\n {t('filePreview.videoNotSupported', 'Votre navigateur ne supporte pas la lecture vidéo.')}\r\n </video>\r\n )}\r\n </div>\r\n );\r\n\r\n case 'audio':\r\n return (\r\n <div className=\"flex flex-col items-center justify-center h-full gap-6 p-4\">\r\n <div className=\"w-24 h-24 rounded-full bg-[var(--color-accent-500)]/20 flex items-center justify-center\">\r\n <File className=\"w-12 h-12 text-[var(--color-accent-500)]\" />\r\n </div>\r\n <p className=\"text-lg font-medium text-[var(--text-primary)]\">{attachment.fileName}</p>\r\n {!blobUrl ? (\r\n <div className=\"flex items-center justify-center\">\r\n <Loader2 className=\"w-8 h-8 animate-spin text-[var(--color-accent-500)]\" />\r\n </div>\r\n ) : (\r\n <audio\r\n src={blobUrl}\r\n controls\r\n className=\"w-full max-w-md\"\r\n >\r\n {t('filePreview.audioNotSupported', 'Votre navigateur ne supporte pas la lecture audio.')}\r\n </audio>\r\n )}\r\n </div>\r\n );\r\n\r\n default:\r\n return (\r\n <div className=\"flex flex-col items-center justify-center h-full gap-4 text-center px-8\">\r\n <div className=\"w-20 h-20 rounded-xl bg-[var(--bg-secondary)] flex items-center justify-center\">\r\n <FileText className=\"w-10 h-10 text-[var(--text-tertiary)]\" />\r\n </div>\r\n <div>\r\n <p className=\"text-lg font-medium text-[var(--text-primary)] mb-1\">\r\n {attachment.fileName}\r\n </p>\r\n <p className=\"text-sm text-[var(--text-tertiary)]\">\r\n {formatFileSize(attachment.fileSize)}\r\n </p>\r\n </div>\r\n <p className=\"text-[var(--text-secondary)]\">\r\n {t('filePreview.noPreview', 'Aperçu non disponible pour ce type de fichier')}\r\n </p>\r\n <button\r\n onClick={() => onDownload(attachment)}\r\n className=\"btn btn-primary\"\r\n >\r\n <Download className=\"w-4 h-4 mr-2\" />\r\n {t('filePreview.download', 'Télécharger le fichier')}\r\n </button>\r\n </div>\r\n );\r\n }\r\n };\r\n\r\n const modal = (\r\n <div\r\n className=\"fixed inset-0 bg-black/90 backdrop-blur-sm flex flex-col z-[100]\"\r\n onClick={onClose}\r\n onKeyDown={(e) => { if (e.key === 'Escape') onClose(); }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label={attachment.fileName}\r\n >\r\n {/* Header */}\r\n <div\r\n className=\"flex items-center justify-between px-4 py-3 bg-black/50\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <div className=\"flex items-center gap-3 min-w-0\">\r\n <FileText className=\"w-5 h-5 text-white/70 flex-shrink-0\" />\r\n <div className=\"min-w-0\">\r\n <p className=\"text-white font-medium truncate\">{attachment.fileName}</p>\r\n <p className=\"text-xs text-white/50\">{formatFileSize(attachment.fileSize)}</p>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-2\">\r\n {/* Zoom controls (for images only) */}\r\n {fileType === 'image' && (\r\n <>\r\n <button\r\n onClick={handleZoomOut}\r\n className=\"p-2 text-white/70 hover:text-white hover:bg-white/10 rounded-lg transition-colors\"\r\n title={t('filePreview.zoomOut', 'Zoom arrière (-)') || 'Zoom out'}\r\n >\r\n <ZoomOut className=\"w-5 h-5\" />\r\n </button>\r\n <span className=\"text-white/70 text-sm min-w-[3rem] text-center\">\r\n {Math.round(zoom * 100)}%\r\n </span>\r\n <button\r\n onClick={handleZoomIn}\r\n className=\"p-2 text-white/70 hover:text-white hover:bg-white/10 rounded-lg transition-colors\"\r\n title={t('filePreview.zoomIn', 'Zoom avant (+)') || 'Zoom in'}\r\n >\r\n <ZoomIn className=\"w-5 h-5\" />\r\n </button>\r\n <button\r\n onClick={handleRotate}\r\n className=\"p-2 text-white/70 hover:text-white hover:bg-white/10 rounded-lg transition-colors\"\r\n title={t('filePreview.rotate', 'Rotation (R)') || 'Rotate'}\r\n >\r\n <RotateCw className=\"w-5 h-5\" />\r\n </button>\r\n <div className=\"w-px h-6 bg-white/20 mx-1\" />\r\n </>\r\n )}\r\n\r\n {/* Fullscreen toggle */}\r\n <button\r\n onClick={() => setIsFullscreen(f => !f)}\r\n className=\"p-2 text-white/70 hover:text-white hover:bg-white/10 rounded-lg transition-colors\"\r\n title={t('filePreview.fullscreen', 'Plein écran (F)') || 'Fullscreen'}\r\n >\r\n {isFullscreen ? <Minimize2 className=\"w-5 h-5\" /> : <Maximize2 className=\"w-5 h-5\" />}\r\n </button>\r\n\r\n {/* Download */}\r\n <button\r\n onClick={() => onDownload(attachment)}\r\n className=\"p-2 text-white/70 hover:text-white hover:bg-white/10 rounded-lg transition-colors\"\r\n title={t('filePreview.download', 'Télécharger') || 'Download'}\r\n >\r\n <Download className=\"w-5 h-5\" />\r\n </button>\r\n\r\n {/* Close */}\r\n <button\r\n onClick={onClose}\r\n className=\"p-2 text-white/70 hover:text-white hover:bg-white/10 rounded-lg transition-colors\"\r\n title={t('common.close', 'Fermer (Échap)') || 'Close'}\r\n >\r\n <X className=\"w-5 h-5\" />\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {/* Content */}\r\n <div\r\n className=\"flex-1 overflow-hidden\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n {renderContent()}\r\n </div>\r\n\r\n {/* Keyboard shortcuts hint */}\r\n <div\r\n className=\"px-4 py-2 bg-black/50 text-center\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <p className=\"text-xs text-white/40\">\r\n {t('filePreview.shortcuts', 'Raccourcis: Échap pour fermer • +/- pour zoomer • R pour rotation • F pour plein écran')}\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n\r\n return createPortal(modal, document.body);\r\n}\r\n","import { useState, useRef, useCallback, useEffect } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport {\r\n FileText, Monitor, Bug, Image as ImageIcon, File,\r\n Download, Globe, Laptop, Smartphone,\r\n AlertTriangle, User, UserPlus, ChevronDown, ChevronRight,\r\n CheckCircle, Loader2, XCircle, RotateCcw, Plus, Eye,\r\n ArrowUpCircle, History, MessageSquare, UserCheck, Play, Pause, Edit\r\n} from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport type { TicketStatus } from '@/services/api/ticketApi';\r\nimport { FilePreviewModal } from './FilePreviewModal';\r\nimport { apiClient } from '@/services/api/apiClient';\r\n\r\ninterface Attachment {\r\n id: string;\r\n fileName: string;\r\n contentType?: string;\r\n fileSize: number;\r\n createdAt: string;\r\n}\r\n\r\ninterface Activity {\r\n action: string;\r\n newValue?: string | null;\r\n createdAt: string;\r\n}\r\n\r\ninterface ClientContext {\r\n sourceUrl?: string;\r\n browser?: {\r\n name: string;\r\n version: string;\r\n language?: string;\r\n };\r\n os?: {\r\n name: string;\r\n version: string;\r\n };\r\n device?: {\r\n type: string;\r\n screenResolution?: string;\r\n };\r\n recentErrors?: Array<{\r\n message: string;\r\n stack?: string;\r\n timestamp?: string;\r\n component?: string;\r\n }>;\r\n}\r\n\r\ninterface TicketData {\r\n id: string;\r\n ticketNumber: string;\r\n title: string;\r\n description: string;\r\n type: string;\r\n status: string;\r\n priority: string;\r\n createdByName?: string;\r\n assignedToName?: string;\r\n assignees?: Array<{\r\n userId: string;\r\n fullName: string;\r\n isPrimary: boolean;\r\n assignedViaGroupName?: string;\r\n }>;\r\n resolution?: string;\r\n resolvedAt?: string;\r\n closedAt?: string;\r\n createdAt: string;\r\n updatedAt?: string;\r\n clientContext?: ClientContext;\r\n}\r\n\r\ntype SidebarLayout = 'compact' | 'grid';\r\n\r\ninterface TicketSidebarProps {\r\n readonly ticket: TicketData;\r\n readonly attachments: Attachment[];\r\n readonly activities: Activity[];\r\n readonly isSupport: boolean;\r\n readonly saving?: boolean;\r\n readonly uploading?: boolean;\r\n readonly isReadOnly?: boolean;\r\n readonly layout?: SidebarLayout;\r\n readonly onAssign?: () => void;\r\n readonly onStatusChange?: (status: TicketStatus) => void;\r\n readonly onResolve?: () => void;\r\n readonly onCloseTicket?: () => void;\r\n readonly onReopen?: () => void;\r\n readonly onEscalate?: () => void;\r\n readonly onDownloadAttachment: (attachment: Attachment) => void;\r\n readonly onUploadAttachment?: (file: File) => Promise<void>;\r\n readonly getAttachmentUrl: (attachment: Attachment) => string;\r\n}\r\n\r\nconst priorityColors: Record<string, { bg: string; text: string }> = {\r\n Low: { bg: 'var(--info-bg)', text: 'var(--info-text)' },\r\n Medium: { bg: 'var(--warning-bg)', text: 'var(--warning-text)' },\r\n High: { bg: 'var(--error-bg)', text: 'var(--error-text)' },\r\n Critical: { bg: 'var(--error-text)', text: 'white' },\r\n};\r\n\r\nconst formatFileSize = (bytes: number): string => {\r\n if (bytes < 1024) return `${bytes} B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\r\n};\r\n\r\nconst isImageFile = (contentType?: string): boolean => contentType?.startsWith('image/') || false;\r\n\r\n// Component to load authenticated images as blob URLs\r\nfunction AuthenticatedImage({\r\n src,\r\n alt,\r\n className,\r\n onClick,\r\n}: {\r\n src: string;\r\n alt: string;\r\n className?: string;\r\n onClick?: () => void;\r\n}) {\r\n const [blobUrl, setBlobUrl] = useState<string | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState(false);\r\n\r\n useEffect(() => {\r\n let isMounted = true;\r\n const controller = new AbortController();\r\n\r\n apiClient.get<Blob>(src, {\r\n responseType: 'blob',\r\n signal: controller.signal,\r\n })\r\n .then(response => {\r\n if (isMounted) {\r\n const url = URL.createObjectURL(response.data);\r\n setBlobUrl(url);\r\n setLoading(false);\r\n }\r\n })\r\n .catch(() => {\r\n if (isMounted) {\r\n setError(true);\r\n setLoading(false);\r\n }\r\n });\r\n\r\n return () => {\r\n isMounted = false;\r\n controller.abort();\r\n if (blobUrl) {\r\n URL.revokeObjectURL(blobUrl);\r\n }\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [src]);\r\n\r\n if (loading) {\r\n return (\r\n <div className={`flex items-center justify-center bg-[var(--bg-secondary)] ${className}`}>\r\n <Loader2 className=\"w-4 h-4 animate-spin text-[var(--text-tertiary)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n if (error || !blobUrl) {\r\n return (\r\n <div className={`flex items-center justify-center bg-[var(--bg-secondary)] ${className}`}>\r\n <ImageIcon className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <img\r\n src={blobUrl}\r\n alt={alt}\r\n className={className}\r\n onClick={onClick}\r\n />\r\n );\r\n}\r\n\r\ntype Section = 'info' | 'system' | 'errors' | 'attachments' | 'actions' | 'history';\r\n\r\nconst activityIconConfig: Record<string, { icon: React.ElementType; color: string }> = {\r\n Created: { icon: Plus, color: 'var(--success-text)' },\r\n StatusChanged: { icon: Edit, color: 'var(--info-text)' },\r\n Assigned: { icon: UserCheck, color: 'var(--color-accent-600)' },\r\n Unassigned: { icon: User, color: 'var(--warning-text)' },\r\n Commented: { icon: MessageSquare, color: 'var(--text-secondary)' },\r\n PriorityChanged: { icon: AlertTriangle, color: 'var(--warning-text)' },\r\n Resolved: { icon: CheckCircle, color: 'var(--success-text)' },\r\n Closed: { icon: XCircle, color: 'var(--text-tertiary)' },\r\n Reopened: { icon: RotateCcw, color: 'var(--warning-text)' },\r\n Escalated: { icon: ArrowUpCircle, color: 'var(--error-text)' },\r\n Started: { icon: Play, color: 'var(--info-text)' },\r\n Paused: { icon: Pause, color: 'var(--warning-text)' },\r\n};\r\n\r\ntype TFn = (key: string, options?: Record<string, unknown>) => string;\r\n\r\nconst formatActivityDate = (dateString: string, t: TFn): string => {\r\n const date = new Date(dateString);\r\n const now = new Date();\r\n const diffMs = now.getTime() - date.getTime();\r\n const diffMins = Math.floor(diffMs / 60000);\r\n const diffHours = Math.floor(diffMs / 3600000);\r\n const diffDays = Math.floor(diffMs / 86400000);\r\n\r\n if (diffMins < 1) return t('sidebar.timeAgo.justNow', { defaultValue: 'Just now' });\r\n if (diffMins < 60) return t('sidebar.timeAgo.minutes', { count: diffMins });\r\n if (diffHours < 24) return t('sidebar.timeAgo.hours', { count: diffHours });\r\n if (diffDays < 7) return t('sidebar.timeAgo.days', { count: diffDays });\r\n return date.toLocaleDateString(undefined, { day: 'numeric', month: 'short' });\r\n};\r\n\r\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\r\nconst ALLOWED_EXTENSIONS = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.txt', '.csv', '.zip', '.png', '.jpg', '.jpeg', '.gif', '.webp'];\r\n\r\ninterface SectionHeaderProps {\r\n readonly section: Section;\r\n readonly icon: React.ElementType;\r\n readonly title: string;\r\n readonly count?: number;\r\n readonly isExpanded: boolean;\r\n readonly onToggle: (section: Section) => void;\r\n}\r\n\r\nfunction SectionHeader({\r\n section,\r\n icon: Icon,\r\n title,\r\n count,\r\n isExpanded,\r\n onToggle,\r\n}: SectionHeaderProps) {\r\n return (\r\n <button\r\n onClick={() => onToggle(section)}\r\n className=\"w-full flex items-center gap-2 py-2 px-3 hover:bg-[var(--bg-secondary)] transition-colors rounded-lg\"\r\n >\r\n {isExpanded ? (\r\n <ChevronDown className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n ) : (\r\n <ChevronRight className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n )}\r\n <Icon className=\"w-4 h-4 text-[var(--color-accent-600)]\" />\r\n <span className=\"font-medium text-sm flex-1 text-left\">{title}</span>\r\n {count !== undefined && count > 0 && (\r\n <span className=\"text-xs px-1.5 py-0.5 rounded-full bg-[var(--bg-tertiary)] text-[var(--text-secondary)]\">\r\n {count}\r\n </span>\r\n )}\r\n </button>\r\n );\r\n}\r\n\r\nexport function TicketSidebar({\r\n ticket,\r\n attachments,\r\n activities,\r\n isSupport,\r\n saving,\r\n uploading,\r\n layout = 'compact',\r\n onAssign,\r\n onStatusChange,\r\n onResolve,\r\n onCloseTicket,\r\n onReopen,\r\n onEscalate,\r\n onDownloadAttachment,\r\n onUploadAttachment,\r\n getAttachmentUrl,\r\n}: TicketSidebarProps): ReactElement {\r\n const { t } = useTranslation('support');\r\n const [expandedSections, setExpandedSections] = useState<Set<Section>>(\r\n new Set(layout === 'grid'\r\n ? ['info', 'system', 'errors', 'attachments', 'actions', 'history']\r\n : ['info', 'attachments', 'actions']\r\n )\r\n );\r\n const [previewFile, setPreviewFile] = useState<Attachment | null>(null);\r\n const [isDragging, setIsDragging] = useState(false);\r\n const [uploadError, setUploadError] = useState<string | null>(null);\r\n const fileInputRef = useRef<HTMLInputElement>(null);\r\n\r\n const isAllowedFile = useCallback((file: File): boolean => {\r\n if (file.type.startsWith('image/')) return true;\r\n const ext = '.' + file.name.split('.').pop()?.toLowerCase();\r\n return ALLOWED_EXTENSIONS.includes(ext);\r\n }, []);\r\n\r\n const handleFileSelect = useCallback(async (files: FileList | null) => {\r\n if (!files || !onUploadAttachment) return;\r\n setUploadError(null);\r\n\r\n for (let i = 0; i < files.length; i++) {\r\n const file = files[i];\r\n if (file.size > MAX_FILE_SIZE) {\r\n setUploadError(t('sidebar.fileTooLarge', 'File too large (max 10 MB)'));\r\n continue;\r\n }\r\n if (!isAllowedFile(file)) {\r\n setUploadError(t('sidebar.fileTypeNotAllowed', 'File type not allowed'));\r\n continue;\r\n }\r\n try {\r\n await onUploadAttachment(file);\r\n } catch {\r\n setUploadError(t('sidebar.uploadError', 'Upload failed'));\r\n }\r\n }\r\n }, [onUploadAttachment, isAllowedFile, t]);\r\n\r\n const handleDragOver = useCallback((e: React.DragEvent) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n }, []);\r\n\r\n const handleDragEnter = useCallback((e: React.DragEvent) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n setIsDragging(true);\r\n }, []);\r\n\r\n const handleDragLeave = useCallback((e: React.DragEvent) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n setIsDragging(false);\r\n }, []);\r\n\r\n const handleDrop = useCallback((e: React.DragEvent) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n setIsDragging(false);\r\n handleFileSelect(e.dataTransfer.files);\r\n }, [handleFileSelect]);\r\n\r\n const imageAttachments = attachments.filter(a => isImageFile(a.contentType));\r\n const docAttachments = attachments.filter(a => !isImageFile(a.contentType));\r\n const clientContext = ticket.clientContext;\r\n\r\n const toggleSection = useCallback((section: Section) => {\r\n setExpandedSections(prev => {\r\n const next = new Set(prev);\r\n if (next.has(section)) {\r\n next.delete(section);\r\n } else {\r\n next.add(section);\r\n }\r\n return next;\r\n });\r\n }, []);\r\n\r\n const isClosed = ticket.status === 'Closed' || ticket.status === 'Rejected';\r\n\r\n const isGrid = layout === 'grid';\r\n const sectionClass = isGrid\r\n ? 'bg-[var(--bg-card)] rounded-lg border border-[var(--border-color)] overflow-hidden'\r\n : 'px-2 py-1 border-b border-[var(--border-color)]';\r\n\r\n return (\r\n <div className={isGrid ? '' : 'h-full flex flex-col bg-[var(--bg-card)] overflow-hidden'}>\r\n {/* Scrollable content */}\r\n <div className={isGrid\r\n ? 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4'\r\n : 'flex-1 overflow-y-auto'\r\n }>\r\n {/* Info section */}\r\n <div className={sectionClass}>\r\n <SectionHeader section=\"info\" icon={FileText} title={t('details.info', 'Informations')} isExpanded={expandedSections.has('info')} onToggle={toggleSection} />\r\n {expandedSections.has('info') && (\r\n <div className=\"px-3 pb-3\">\r\n <dl className=\"space-y-2 text-sm\">\r\n <div className=\"flex justify-between\">\r\n <dt className=\"text-[var(--text-tertiary)]\">{t('sidebar.type', 'Type')}</dt>\r\n <dd className=\"font-medium\">{t(`tickets.types.${ticket.type}`, { defaultValue: ticket.type })}</dd>\r\n </div>\r\n <div className=\"flex justify-between items-center\">\r\n <dt className=\"text-[var(--text-tertiary)]\">{t('sidebar.priority', 'Priority')}</dt>\r\n <dd>\r\n <span\r\n className=\"inline-block px-2 py-0.5 rounded text-xs font-medium\"\r\n style={{\r\n backgroundColor: priorityColors[ticket.priority]?.bg,\r\n color: priorityColors[ticket.priority]?.text,\r\n }}\r\n >\r\n {t(`tickets.priorities.${ticket.priority}`, { defaultValue: ticket.priority })}\r\n </span>\r\n </dd>\r\n </div>\r\n {isSupport && (\r\n <>\r\n <div className=\"flex justify-between\">\r\n <dt className=\"text-[var(--text-tertiary)]\">{t('sidebar.createdBy', 'Created by')}</dt>\r\n <dd>{ticket.createdByName || t('sidebar.unknown', 'Unknown')}</dd>\r\n </div>\r\n <div className=\"flex justify-between items-start\">\r\n <dt className=\"text-[var(--text-tertiary)]\">{t('sidebar.assignedTo', 'Assigned to')}</dt>\r\n <dd className=\"flex flex-col items-end gap-1\">\r\n {(ticket?.assignees?.length ?? 0) > 0 ? (\r\n <div className=\"space-y-1\">\r\n {ticket?.assignees?.map((assignee) => (\r\n <div key={assignee.userId} className=\"flex items-center gap-1\">\r\n <span className=\"text-sm\">{assignee.fullName}</span>\r\n {assignee.isPrimary && (\r\n <span className=\"text-xs px-1.5 py-0.5 bg-[var(--color-accent-600)] text-white rounded-md\">\r\n {t('sidebar.primary', 'primary')}\r\n </span>\r\n )}\r\n {assignee.assignedViaGroupName && (\r\n <span className=\"text-xs px-1.5 py-0.5 bg-[var(--bg-secondary)] text-[var(--text-secondary)] rounded-md\">\r\n ({t('sidebar.viaGroup', { groupName: assignee.assignedViaGroupName })})\r\n </span>\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n ) : (\r\n <span className=\"text-sm\">{t('sidebar.unassigned', 'Unassigned')}</span>\r\n )}\r\n {onAssign && (\r\n <button onClick={onAssign} className=\"p-1 hover:bg-[var(--bg-secondary)] rounded mt-1\">\r\n <UserPlus className=\"w-3.5 h-3.5 text-[var(--color-accent-600)]\" />\r\n </button>\r\n )}\r\n </dd>\r\n </div>\r\n </>\r\n )}\r\n <div className=\"flex justify-between\">\r\n <dt className=\"text-[var(--text-tertiary)]\">{t('sidebar.createdAt', 'Created')}</dt>\r\n <dd className=\"text-xs\">{new Date(ticket.createdAt).toLocaleString()}</dd>\r\n </div>\r\n {ticket.updatedAt && (\r\n <div className=\"flex justify-between\">\r\n <dt className=\"text-[var(--text-tertiary)]\">{t('sidebar.updatedAt', 'Updated')}</dt>\r\n <dd className=\"text-xs\">{new Date(ticket.updatedAt).toLocaleString()}</dd>\r\n </div>\r\n )}\r\n </dl>\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* System Info (support only) */}\r\n {isSupport && clientContext && (\r\n <div className={sectionClass}>\r\n <SectionHeader section=\"system\" icon={Monitor} title={t('details.systemInfo', 'Infos système')} isExpanded={expandedSections.has('system')} onToggle={toggleSection} />\r\n {expandedSections.has('system') && (\r\n <div className=\"px-3 pb-3 space-y-2\">\r\n {clientContext?.sourceUrl && (\r\n <div className=\"p-2 bg-[var(--bg-secondary)] rounded-lg\">\r\n <div className=\"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)] mb-1\">\r\n <Globe className=\"w-3 h-3\" />\r\n {t('sidebar.url', 'URL')}\r\n </div>\r\n <p className=\"text-xs font-mono break-all\">{clientContext.sourceUrl}</p>\r\n </div>\r\n )}\r\n <div className=\"grid grid-cols-2 gap-2\">\r\n {clientContext.browser && (\r\n <div className=\"p-2 bg-[var(--bg-secondary)] rounded-lg\">\r\n <div className=\"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)] mb-1\">\r\n <Globe className=\"w-3 h-3\" />\r\n {t('sidebar.browser', 'Browser')}\r\n </div>\r\n <p className=\"text-xs font-medium\">{clientContext.browser.name}</p>\r\n <p className=\"text-xs text-[var(--text-tertiary)]\">{clientContext.browser.version}</p>\r\n </div>\r\n )}\r\n {clientContext.os && (\r\n <div className=\"p-2 bg-[var(--bg-secondary)] rounded-lg\">\r\n <div className=\"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)] mb-1\">\r\n <Laptop className=\"w-3 h-3\" />\r\n {t('sidebar.os', 'OS')}\r\n </div>\r\n <p className=\"text-xs font-medium\">{clientContext.os.name}</p>\r\n <p className=\"text-xs text-[var(--text-tertiary)]\">{clientContext.os.version}</p>\r\n </div>\r\n )}\r\n </div>\r\n {clientContext.device && (\r\n <div className=\"p-2 bg-[var(--bg-secondary)] rounded-lg\">\r\n <div className=\"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)] mb-1\">\r\n <Smartphone className=\"w-3 h-3\" />\r\n {t('sidebar.device', 'Device')}\r\n </div>\r\n <p className=\"text-xs font-medium\">\r\n {clientContext.device.type || t('sidebar.defaultDeviceType', 'Desktop')}\r\n {clientContext.device.screenResolution && ` • ${clientContext.device.screenResolution}`}\r\n </p>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Errors (support only) */}\r\n {isSupport && clientContext && (clientContext?.recentErrors?.length ?? 0) > 0 && (\r\n <div className={sectionClass}>\r\n <SectionHeader\r\n section=\"errors\"\r\n icon={Bug}\r\n title={t('details.errors', 'Erreurs')}\r\n count={clientContext?.recentErrors?.length ?? 0}\r\n isExpanded={expandedSections.has('errors')}\r\n onToggle={toggleSection}\r\n />\r\n {expandedSections.has('errors') && (\r\n <div className=\"px-3 pb-3 space-y-2\">\r\n {clientContext?.recentErrors?.slice(0, 3).map((err, index) => (\r\n <div key={`error-${index}`} className=\"p-2 bg-[var(--error-bg)]/30 border border-[var(--error-border)] rounded-lg\">\r\n <div className=\"flex items-center gap-1.5 mb-1\">\r\n <AlertTriangle className=\"w-3 h-3 text-[var(--error-text)]\" />\r\n {err.component && (\r\n <span className=\"text-xs px-1 py-0.5 bg-[var(--bg-secondary)] rounded\">\r\n {err.component}\r\n </span>\r\n )}\r\n </div>\r\n <p className=\"text-xs font-medium text-[var(--error-text)] line-clamp-2\">{err.message}</p>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Attachments */}\r\n <div className={sectionClass}>\r\n <SectionHeader\r\n section=\"attachments\"\r\n icon={File}\r\n title={t('details.attachments', 'Pièces jointes')}\r\n count={attachments.length}\r\n isExpanded={expandedSections.has('attachments')}\r\n onToggle={toggleSection}\r\n />\r\n {expandedSections.has('attachments') && (\r\n <div className=\"px-3 pb-3\">\r\n {/* Images */}\r\n {imageAttachments.length > 0 && (\r\n <div className=\"mb-3\">\r\n <p className=\"text-xs text-[var(--text-tertiary)] mb-2 flex items-center gap-1\">\r\n <ImageIcon className=\"w-3 h-3\" />\r\n {t('sidebar.images', 'Images')} ({imageAttachments.length})\r\n </p>\r\n <div className=\"grid grid-cols-3 gap-1.5\">\r\n {imageAttachments.map(img => (\r\n <button\r\n type=\"button\"\r\n key={img.id}\r\n className=\"relative cursor-pointer rounded overflow-hidden border border-[var(--border-color)] aspect-square group\"\r\n onClick={() => setPreviewFile(img)}\r\n >\r\n <AuthenticatedImage\r\n src={getAttachmentUrl(img)}\r\n alt={img.fileName}\r\n className=\"w-full h-full object-cover\"\r\n />\r\n <div className=\"absolute inset-0 bg-black/0 group-hover:bg-black/30 transition-colors flex items-center justify-center\">\r\n <Eye className=\"w-5 h-5 text-white opacity-0 group-hover:opacity-100 transition-opacity\" />\r\n </div>\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Documents */}\r\n {docAttachments.length > 0 && (\r\n <div className=\"mb-3\">\r\n <p className=\"text-xs text-[var(--text-tertiary)] mb-2 flex items-center gap-1\">\r\n <FileText className=\"w-3 h-3\" />\r\n {t('sidebar.documents', 'Documents')} ({docAttachments.length})\r\n </p>\r\n <div className=\"space-y-1.5\">\r\n {docAttachments.map(doc => (\r\n <div\r\n key={doc.id}\r\n className=\"flex items-center gap-2 w-full p-2 bg-[var(--bg-secondary)] rounded-lg hover:bg-[var(--bg-tertiary)] transition-colors group\"\r\n >\r\n <button\r\n onClick={() => setPreviewFile(doc)}\r\n className=\"flex items-center gap-2 flex-1 min-w-0 text-left\"\r\n >\r\n <FileText className=\"w-4 h-4 text-[var(--text-secondary)] flex-shrink-0\" />\r\n <div className=\"flex-1 min-w-0\">\r\n <span className=\"text-xs block truncate\">{doc.fileName}</span>\r\n <span className=\"text-[10px] text-[var(--text-tertiary)]\">{formatFileSize(doc.fileSize)}</span>\r\n </div>\r\n </button>\r\n <div className=\"flex items-center gap-1\">\r\n <button\r\n onClick={() => setPreviewFile(doc)}\r\n className=\"p-1 rounded hover:bg-[var(--bg-primary)] transition-colors opacity-0 group-hover:opacity-100\"\r\n title={t('filePreview.view', 'Aperçu') || 'View'}\r\n >\r\n <Eye className=\"w-3.5 h-3.5 text-[var(--color-accent-500)]\" />\r\n </button>\r\n <button\r\n onClick={() => onDownloadAttachment(doc)}\r\n className=\"p-1 rounded hover:bg-[var(--bg-primary)] transition-colors\"\r\n title={t('filePreview.download', 'Télécharger') || 'Download'}\r\n >\r\n <Download className=\"w-3.5 h-3.5 text-[var(--text-tertiary)]\" />\r\n </button>\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Upload zone (support only, not closed) */}\r\n {isSupport && !isClosed && onUploadAttachment && (\r\n <div>\r\n <input\r\n ref={fileInputRef}\r\n type=\"file\"\r\n multiple\r\n onChange={(e) => handleFileSelect(e.target.files)}\r\n accept={ALLOWED_EXTENSIONS.join(',')}\r\n className=\"hidden\"\r\n />\r\n <div\r\n className={`border-2 border-dashed rounded-lg p-3 text-center transition-colors cursor-pointer ${\r\n isDragging\r\n ? 'border-[var(--color-accent-500)] bg-[var(--color-accent-500)]/10'\r\n : 'border-[var(--border-color)] hover:border-[var(--color-accent-500)]'\r\n }`}\r\n onClick={() => fileInputRef.current?.click()}\r\n onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); fileInputRef.current?.click(); } }}\r\n role=\"button\"\r\n tabIndex={0}\r\n onDragOver={handleDragOver}\r\n onDragEnter={handleDragEnter}\r\n onDragLeave={handleDragLeave}\r\n onDrop={handleDrop}\r\n >\r\n {uploading ? (\r\n <div className=\"flex items-center justify-center gap-2 py-1\">\r\n <Loader2 className=\"w-4 h-4 animate-spin text-[var(--color-accent-500)]\" />\r\n <span className=\"text-xs text-[var(--text-secondary)]\">{t('sidebar.uploading', 'Uploading...')}</span>\r\n </div>\r\n ) : (\r\n <>\r\n <Plus className=\"w-5 h-5 mx-auto mb-1 text-[var(--text-tertiary)]\" />\r\n <p className=\"text-xs text-[var(--text-secondary)]\">\r\n {isDragging ? t('sidebar.dropHere', 'Drop here') : t('sidebar.addFile', 'Add a file')}\r\n </p>\r\n </>\r\n )}\r\n </div>\r\n {uploadError && (\r\n <p className=\"mt-2 text-xs text-[var(--error-text)]\">{uploadError}</p>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Empty state */}\r\n {attachments.length === 0 && (!isSupport || isClosed || !onUploadAttachment) && (\r\n <p className=\"text-xs text-[var(--text-tertiary)] text-center py-2\">\r\n {t('sidebar.noAttachments', 'No attachments')}\r\n </p>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Actions (support only) — hidden in grid layout (information tab) to avoid duplication with top workflow bar */}\r\n {isSupport && !isGrid && (\r\n <div className={sectionClass}>\r\n <SectionHeader section=\"actions\" icon={User} title={t('details.actions', 'Actions')} isExpanded={expandedSections.has('actions')} onToggle={toggleSection} />\r\n {expandedSections.has('actions') && (\r\n <div className=\"px-3 pb-3 space-y-2\">\r\n {ticket.status === 'Open' && onStatusChange && (\r\n <button\r\n onClick={() => onStatusChange('InProgress')}\r\n disabled={saving}\r\n className=\"btn btn-secondary w-full text-sm py-2\"\r\n >\r\n {t('sidebar.startProcessing', 'Start processing')}\r\n </button>\r\n )}\r\n {ticket.status === 'InProgress' && (\r\n <>\r\n {onStatusChange && (\r\n <button\r\n onClick={() => onStatusChange('OnHold')}\r\n disabled={saving}\r\n className=\"btn btn-secondary w-full text-sm py-2\"\r\n >\r\n {t('sidebar.putOnHold', 'Put on hold')}\r\n </button>\r\n )}\r\n {onResolve && (\r\n <button\r\n onClick={onResolve}\r\n disabled={saving}\r\n className=\"btn btn-primary w-full text-sm py-2\"\r\n >\r\n <CheckCircle className=\"w-4 h-4 mr-2\" />\r\n {t('sidebar.resolve', 'Resolve')}\r\n </button>\r\n )}\r\n </>\r\n )}\r\n {ticket.status === 'OnHold' && onStatusChange && (\r\n <button\r\n onClick={() => onStatusChange('InProgress')}\r\n disabled={saving}\r\n className=\"btn btn-secondary w-full text-sm py-2\"\r\n >\r\n {t('sidebar.resume', 'Resume')}\r\n </button>\r\n )}\r\n {ticket.status === 'Resolved' && onCloseTicket && (\r\n <button\r\n onClick={onCloseTicket}\r\n disabled={saving}\r\n className=\"btn btn-primary w-full text-sm py-2\"\r\n >\r\n <XCircle className=\"w-4 h-4 mr-2\" />\r\n {t('sidebar.closeTicket', 'Close ticket')}\r\n </button>\r\n )}\r\n {(ticket.status === 'Closed' || ticket.status === 'Resolved') && onReopen && (\r\n <button\r\n onClick={onReopen}\r\n disabled={saving}\r\n className=\"btn btn-secondary w-full text-sm py-2\"\r\n >\r\n <RotateCcw className=\"w-4 h-4 mr-2\" />\r\n {t('sidebar.reopen', 'Reopen')}\r\n </button>\r\n )}\r\n {/* Escalation button - available when not closed */}\r\n {!isClosed && onEscalate && (\r\n <button\r\n onClick={onEscalate}\r\n disabled={saving}\r\n className=\"btn w-full text-sm py-2 border border-[var(--warning-border)] bg-[var(--warning-bg)] text-[var(--warning-text)] hover:bg-[var(--warning-border)]\"\r\n >\r\n <ArrowUpCircle className=\"w-4 h-4 mr-2\" />\r\n {t('sidebar.escalate', 'Escalate')}\r\n </button>\r\n )}\r\n {saving && (\r\n <div className=\"flex items-center justify-center py-2\">\r\n <Loader2 className=\"w-5 h-5 animate-spin text-[var(--color-primary-600)]\" />\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* History (support only) */}\r\n {isSupport && activities.length > 0 && (\r\n <div className={isGrid ? sectionClass : 'px-2 py-1'}>\r\n <SectionHeader\r\n section=\"history\"\r\n icon={History}\r\n title={t('details.history', 'History')}\r\n count={activities.length}\r\n isExpanded={expandedSections.has('history')}\r\n onToggle={toggleSection}\r\n />\r\n {expandedSections.has('history') && (\r\n <div className=\"px-3 pb-3\">\r\n <div className=\"relative\">\r\n {/* Timeline line */}\r\n <div className=\"absolute left-3 top-2 bottom-2 w-0.5 bg-[var(--border-color)]\" />\r\n\r\n {/* Activities list */}\r\n <div className=\"space-y-3\">\r\n {activities.slice(0, 10).map((activity, idx) => {\r\n const config = activityIconConfig[activity.action] || {\r\n icon: Edit,\r\n color: 'var(--text-secondary)',\r\n };\r\n const Icon = config.icon;\r\n const label = t(`history.actions.${activity.action}`, { defaultValue: activity.action });\r\n\r\n return (\r\n <div key={`activity-${activity.action}-${idx}`} className=\"relative flex gap-3 pl-1\">\r\n {/* Icon */}\r\n <div\r\n className=\"relative z-10 w-5 h-5 rounded-full flex items-center justify-center bg-[var(--bg-card)] border border-[var(--border-color)]\"\r\n style={{ color: config.color }}\r\n >\r\n <Icon className=\"w-3 h-3\" />\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 min-w-0\">\r\n <p className=\"text-xs font-medium text-[var(--text-primary)]\">\r\n {label}\r\n {activity.newValue && (\r\n <span className=\"text-[var(--text-secondary)] font-normal\">\r\n {' → '}{activity.newValue}\r\n </span>\r\n )}\r\n </p>\r\n <p className=\"text-[10px] text-[var(--text-tertiary)]\">\r\n {formatActivityDate(activity.createdAt, t)}\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n\r\n {/* Show more indicator */}\r\n {activities.length > 10 && (\r\n <p className=\"text-xs text-[var(--text-tertiary)] text-center mt-3\">\r\n {t('sidebar.moreActivities', { count: activities.length - 10 })}\r\n </p>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* File preview modal */}\r\n <FilePreviewModal\r\n attachment={previewFile}\r\n getUrl={getAttachmentUrl}\r\n onClose={() => setPreviewFile(null)}\r\n onDownload={onDownloadAttachment}\r\n />\r\n </div>\r\n );\r\n}\r\n","import type { ReactElement } from 'react';\r\nimport {\r\n Clock,\r\n Play,\r\n Pause,\r\n CheckCircle,\r\n XCircle,\r\n AlertTriangle,\r\n RotateCcw,\r\n UserPlus,\r\n ArrowUpCircle,\r\n Loader2,\r\n} from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport type { TicketStatus } from '@/services/api/ticketApi';\r\n\r\nexport type TicketStatusType =\r\n | 'Open'\r\n | 'InProgress'\r\n | 'OnHold'\r\n | 'Resolved'\r\n | 'Closed'\r\n | 'Rejected';\r\n\r\ninterface WorkflowStep {\r\n status: TicketStatusType;\r\n label: string;\r\n icon: React.ElementType;\r\n date?: string;\r\n isActive: boolean;\r\n isPast: boolean;\r\n isFuture: boolean;\r\n}\r\n\r\ninterface Assignee {\r\n userId: string;\r\n fullName: string;\r\n isPrimary: boolean;\r\n assignedViaGroupName?: string;\r\n}\r\n\r\ninterface TicketWorkflowHorizontalProps {\r\n readonly currentStatus: string;\r\n readonly createdAt: string;\r\n readonly resolvedAt?: string;\r\n readonly closedAt?: string;\r\n readonly activities?: Array<{\r\n readonly action: string;\r\n readonly newValue?: string;\r\n readonly createdAt: string;\r\n }>;\r\n readonly isSupport?: boolean;\r\n readonly saving?: boolean;\r\n readonly assignees?: Assignee[];\r\n readonly onAssign?: () => void;\r\n readonly onStatusChange?: (status: TicketStatus) => void;\r\n readonly onResolve?: () => void;\r\n readonly onCloseTicket?: () => void;\r\n readonly onReopen?: () => void;\r\n readonly onEscalate?: () => void;\r\n}\r\n\r\nconst statusOrder: TicketStatusType[] = [\r\n 'Open',\r\n 'InProgress',\r\n 'Resolved',\r\n 'Closed',\r\n];\r\n\r\nconst statusConfig: Record<\r\n TicketStatusType,\r\n { label: string; icon: React.ElementType; color: string }\r\n> = {\r\n Open: {\r\n label: 'Ouvert',\r\n icon: Clock,\r\n color: 'var(--warning-text)',\r\n },\r\n InProgress: {\r\n label: 'En cours',\r\n icon: Play,\r\n color: 'var(--info-text)',\r\n },\r\n OnHold: {\r\n label: 'En attente',\r\n icon: Pause,\r\n color: 'var(--warning-text)',\r\n },\r\n Resolved: {\r\n label: 'Résolu',\r\n icon: CheckCircle,\r\n color: 'var(--success-text)',\r\n },\r\n Closed: {\r\n label: 'Fermé',\r\n icon: XCircle,\r\n color: 'var(--text-secondary)',\r\n },\r\n Rejected: {\r\n label: 'Rejeté',\r\n icon: AlertTriangle,\r\n color: 'var(--error-text)',\r\n },\r\n};\r\n\r\nconst formatShortDate = (dateString: string): string => {\r\n const date = new Date(dateString);\r\n return date.toLocaleDateString('fr-FR', {\r\n day: 'numeric',\r\n month: 'short',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n });\r\n};\r\n\r\nconst getStepCircleClasses = (step: WorkflowStep): string => {\r\n if (step.isActive) return 'ring-4 ring-opacity-30';\r\n if (step.isPast) return 'bg-[var(--success-bg)]';\r\n return 'bg-[var(--bg-secondary)] border-2 border-[var(--border-color)]';\r\n};\r\n\r\nconst getBackgroundColor = (step: WorkflowStep, config: typeof statusConfig[TicketStatusType]): string | undefined => {\r\n if (step.isActive) return config.color;\r\n if (step.isPast) return 'var(--success-bg)';\r\n return undefined;\r\n};\r\n\r\nconst getStepCircleStyle = (step: WorkflowStep, config: typeof statusConfig[TicketStatusType]) => ({\r\n backgroundColor: getBackgroundColor(step, config),\r\n boxShadow: step.isActive ? `0 0 0 4px ${config.color}30` : undefined,\r\n});\r\n\r\nconst getIconColor = (step: WorkflowStep): string => {\r\n if (step.isActive) return 'white';\r\n if (step.isPast) return 'var(--success-text)';\r\n return 'var(--text-tertiary)';\r\n};\r\n\r\nconst getStepLabelClasses = (step: WorkflowStep): string => {\r\n if (step.isActive) return 'text-[var(--text-primary)]';\r\n if (step.isPast) return 'text-[var(--success-text)]';\r\n return 'text-[var(--text-tertiary)]';\r\n};\r\n\r\nconst getConnectorColor = (step: WorkflowStep, config: typeof statusConfig[TicketStatusType]): string => {\r\n if (step.isPast) return 'var(--success-text)';\r\n if (step.isActive) return config.color;\r\n return 'var(--border-color)';\r\n};\r\n\r\ninterface StepCircleProps {\r\n readonly step: WorkflowStep;\r\n readonly config: typeof statusConfig[TicketStatusType];\r\n}\r\n\r\nfunction StepCircle({ step, config }: Readonly<StepCircleProps>) {\r\n const Icon = step.icon;\r\n return (\r\n <div\r\n className={`relative w-10 h-10 rounded-full flex items-center justify-center shrink-0 transition-all ${getStepCircleClasses(step)}`}\r\n style={getStepCircleStyle(step, config)}\r\n >\r\n <Icon className=\"w-5 h-5\" style={{ color: getIconColor(step) }} />\r\n </div>\r\n );\r\n}\r\n\r\ninterface StepLabelProps {\r\n readonly step: WorkflowStep;\r\n}\r\n\r\nfunction StepLabel({ step }: Readonly<StepLabelProps>) {\r\n return (\r\n <div className=\"mt-2 text-center\">\r\n <p className={`text-xs font-medium ${getStepLabelClasses(step)}`}>{step.label}</p>\r\n {step.date && (step.isActive || step.isPast) && (\r\n <p className=\"text-[10px] text-[var(--text-secondary)] mt-0.5\">{formatShortDate(step.date)}</p>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\ninterface StepConnectorProps {\r\n readonly step: WorkflowStep;\r\n readonly config: typeof statusConfig[TicketStatusType];\r\n readonly isLast: boolean;\r\n}\r\n\r\nfunction StepConnector({ step, config, isLast }: Readonly<StepConnectorProps>) {\r\n if (isLast) return null;\r\n return (\r\n <div\r\n className=\"flex-1 h-0.5 mx-2 mt-[-20px]\"\r\n style={{ backgroundColor: getConnectorColor(step, config) }}\r\n />\r\n );\r\n}\r\n\r\nexport function TicketWorkflowHorizontal({\r\n currentStatus,\r\n createdAt,\r\n resolvedAt,\r\n closedAt,\r\n activities = [],\r\n isSupport,\r\n saving,\r\n assignees,\r\n onAssign,\r\n onStatusChange,\r\n onResolve,\r\n onCloseTicket,\r\n onReopen,\r\n onEscalate,\r\n}: TicketWorkflowHorizontalProps): ReactElement {\r\n const { t } = useTranslation('support');\r\n const isRejected = currentStatus === 'Rejected';\r\n const isReopened = activities.some((a) => a.action === 'Reopened');\r\n\r\n const getSteps = (): WorkflowStep[] => {\r\n if (isRejected) {\r\n return [\r\n {\r\n status: 'Open',\r\n label: t('tickets.statuses.Open', statusConfig.Open.label),\r\n icon: statusConfig.Open.icon,\r\n date: createdAt,\r\n isActive: false,\r\n isPast: true,\r\n isFuture: false,\r\n },\r\n {\r\n status: 'Rejected',\r\n label: t('tickets.statuses.Rejected', statusConfig.Rejected.label),\r\n icon: statusConfig.Rejected.icon,\r\n date: closedAt,\r\n isActive: true,\r\n isPast: false,\r\n isFuture: false,\r\n },\r\n ];\r\n }\r\n\r\n const currentIndex = statusOrder.indexOf(currentStatus as TicketStatusType);\r\n const steps: WorkflowStep[] = [];\r\n\r\n const statusDates: Record<string, string> = {\r\n Open: createdAt,\r\n };\r\n\r\n activities\r\n .filter((a) => a.action === 'StatusChanged' && a.newValue)\r\n .forEach((a) => {\r\n if (a.newValue) {\r\n statusDates[a.newValue] = a.createdAt;\r\n }\r\n });\r\n\r\n if (resolvedAt) statusDates['Resolved'] = resolvedAt;\r\n if (closedAt) statusDates['Closed'] = closedAt;\r\n\r\n const wasOnHold = activities.some(\r\n (a) => a.action === 'StatusChanged' && a.newValue === 'OnHold'\r\n );\r\n\r\n statusOrder.forEach((status) => {\r\n const config = statusConfig[status];\r\n const statusIdx =\r\n status === currentStatus\r\n ? currentIndex\r\n : statusOrder.indexOf(status);\r\n const isActive = status === currentStatus;\r\n const isPast = statusIdx < currentIndex;\r\n const isFuture = statusIdx > currentIndex;\r\n\r\n steps.push({\r\n status,\r\n label: t(`tickets.statuses.${status}`, { defaultValue: config.label }),\r\n icon: config.icon,\r\n date: statusDates[status],\r\n isActive,\r\n isPast,\r\n isFuture,\r\n });\r\n });\r\n\r\n if (wasOnHold) {\r\n const onHoldIndex = steps.findIndex((s) => s.status === 'Resolved');\r\n steps.splice(onHoldIndex, 0, {\r\n status: 'OnHold',\r\n label: t('tickets.statuses.OnHold', statusConfig.OnHold.label),\r\n icon: statusConfig.OnHold.icon,\r\n date: statusDates['OnHold'],\r\n isActive: currentStatus === 'OnHold',\r\n isPast:\r\n currentStatus !== 'OnHold' &&\r\n currentStatus !== 'Open' &&\r\n currentStatus !== 'InProgress',\r\n isFuture: false,\r\n });\r\n }\r\n\r\n return steps;\r\n };\r\n\r\n const steps = getSteps();\r\n\r\n return (\r\n <div className=\"w-full px-4 py-2 bg-[var(--bg-card)] border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-4\">\r\n {/* Workflow steps */}\r\n <div className=\"flex items-center flex-1 min-w-0\">\r\n {steps.map((step, index) => {\r\n const config = statusConfig[step.status];\r\n const isLast = index === steps.length - 1;\r\n\r\n return (\r\n <div key={step.status} className=\"flex items-center flex-1\">\r\n <div className={`flex flex-col items-center ${step.isFuture ? 'opacity-40' : ''}`}>\r\n <StepCircle step={step} config={config} />\r\n <StepLabel step={step} />\r\n </div>\r\n <StepConnector step={step} config={config} isLast={isLast} />\r\n </div>\r\n );\r\n })}\r\n {isReopened && (\r\n <div className=\"ml-2 flex items-center gap-1 text-[10px] text-[var(--warning-text)] shrink-0\">\r\n <RotateCcw className=\"w-3 h-3\" />\r\n <span>{t('workflow.reopened', 'Réouvert')}</span>\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Separator */}\r\n {isSupport && (\r\n <div className=\"w-px h-12 bg-[var(--border-color)] shrink-0\" />\r\n )}\r\n\r\n {/* Assignment + Actions */}\r\n {isSupport && (\r\n <div className=\"flex items-center gap-3 shrink-0\">\r\n {/* Assignees */}\r\n <div className=\"flex items-center gap-1.5\">\r\n {(assignees?.length ?? 0) > 0 ? (\r\n <div className=\"flex items-center -space-x-1.5\" title={assignees?.map(a => a.fullName).join(', ')}>\r\n {assignees?.slice(0, 3).map((assignee) => (\r\n <div\r\n key={assignee.userId}\r\n className=\"w-7 h-7 rounded-full bg-[var(--color-accent-500)] flex items-center justify-center text-white text-[10px] font-medium border-2 border-[var(--bg-card)]\"\r\n title={`${assignee.fullName}${assignee.isPrimary ? ` (${t('workflow.primary', 'principal')})` : ''}${assignee.assignedViaGroupName ? ` (${t('workflow.viaGroup', 'via')} ${assignee.assignedViaGroupName})` : ''}`}\r\n >\r\n {assignee.fullName.charAt(0).toUpperCase()}\r\n </div>\r\n ))}\r\n {(assignees?.length ?? 0) > 3 && (\r\n <div className=\"w-7 h-7 rounded-full bg-[var(--bg-tertiary)] flex items-center justify-center text-[10px] font-medium text-[var(--text-secondary)] border-2 border-[var(--bg-card)]\">\r\n +{(assignees?.length ?? 0) - 3}\r\n </div>\r\n )}\r\n </div>\r\n ) : (\r\n <span className=\"text-[10px] text-[var(--text-tertiary)] italic\">{t('workflow.notAssigned', 'Non assigné')}</span>\r\n )}\r\n {onAssign && (\r\n <button\r\n onClick={onAssign}\r\n className=\"w-7 h-7 rounded-full border border-dashed border-[var(--border-color)] flex items-center justify-center hover:border-[var(--color-accent-500)] hover:bg-[var(--bg-secondary)] transition-colors\"\r\n title={t('workflow.assign', 'Assigner')}\r\n >\r\n <UserPlus className=\"w-3.5 h-3.5 text-[var(--color-accent-600)]\" />\r\n </button>\r\n )}\r\n </div>\r\n\r\n {/* Actions */}\r\n {saving && <Loader2 className=\"w-4 h-4 animate-spin text-[var(--color-primary-600)]\" />}\r\n\r\n {currentStatus === 'Open' && onStatusChange && (\r\n <button\r\n onClick={() => onStatusChange('InProgress')}\r\n disabled={saving}\r\n className=\"btn btn-secondary btn-sm text-xs\"\r\n >\r\n <Play className=\"w-3.5 h-3.5 mr-1\" />\r\n {t('workflow.actions.start', 'Commencer')}\r\n </button>\r\n )}\r\n\r\n {currentStatus === 'InProgress' && onStatusChange && (\r\n <button\r\n onClick={() => onStatusChange('OnHold')}\r\n disabled={saving}\r\n className=\"btn btn-secondary btn-sm text-xs\"\r\n >\r\n <Pause className=\"w-3.5 h-3.5 mr-1\" />\r\n {t('workflow.actions.hold', 'En attente')}\r\n </button>\r\n )}\r\n\r\n {currentStatus === 'InProgress' && onResolve && (\r\n <button\r\n onClick={onResolve}\r\n disabled={saving}\r\n className=\"btn btn-primary btn-sm text-xs\"\r\n >\r\n <CheckCircle className=\"w-3.5 h-3.5 mr-1\" />\r\n {t('workflow.actions.resolve', 'Résoudre')}\r\n </button>\r\n )}\r\n\r\n {currentStatus === 'OnHold' && onStatusChange && (\r\n <button\r\n onClick={() => onStatusChange('InProgress')}\r\n disabled={saving}\r\n className=\"btn btn-secondary btn-sm text-xs\"\r\n >\r\n <Play className=\"w-3.5 h-3.5 mr-1\" />\r\n {t('workflow.actions.resume', 'Reprendre')}\r\n </button>\r\n )}\r\n\r\n {currentStatus === 'Resolved' && onCloseTicket && (\r\n <button\r\n onClick={onCloseTicket}\r\n disabled={saving}\r\n className=\"btn btn-primary btn-sm text-xs\"\r\n >\r\n <XCircle className=\"w-3.5 h-3.5 mr-1\" />\r\n {t('workflow.actions.close', 'Fermer')}\r\n </button>\r\n )}\r\n\r\n {(currentStatus === 'Closed' || currentStatus === 'Resolved') && onReopen && (\r\n <button\r\n onClick={onReopen}\r\n disabled={saving}\r\n className=\"btn btn-secondary btn-sm text-xs\"\r\n >\r\n <RotateCcw className=\"w-3.5 h-3.5 mr-1\" />\r\n {t('workflow.actions.reopen', 'Réouvrir')}\r\n </button>\r\n )}\r\n\r\n {currentStatus !== 'Closed' && currentStatus !== 'Rejected' && onEscalate && (\r\n <button\r\n onClick={onEscalate}\r\n disabled={saving}\r\n className=\"btn btn-sm text-xs border border-[var(--warning-border)] bg-[var(--warning-bg)] text-[var(--warning-text)] hover:bg-[var(--warning-border)]\"\r\n >\r\n <ArrowUpCircle className=\"w-3.5 h-3.5 mr-1\" />\r\n {t('workflow.actions.escalate', 'Escalader')}\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState, useCallback } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport {\r\n CheckCircle, XCircle, Clock, AlertCircle, ChevronDown, ChevronRight,\r\n Send, Loader2\r\n} from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport type { TicketSolutionDto } from '@/services/api/ticketApi';\r\n\r\ninterface TicketSolutionPanelProps {\r\n readonly ticketId: string;\r\n readonly solutions: TicketSolutionDto[];\r\n readonly activeSolution?: TicketSolutionDto;\r\n readonly mode: 'support' | 'client';\r\n readonly onProposeSolution?: (content: string) => Promise<void>;\r\n readonly onApproveSolution?: () => Promise<void>;\r\n readonly onRejectSolution?: (reason: string) => Promise<void>;\r\n readonly isLoading?: boolean;\r\n}\r\n\r\nconst statusColorMap: Record<string, { border: string; bg: string; text: string }> = {\r\n Proposed: {\r\n border: 'border-blue-500',\r\n bg: 'bg-blue-50',\r\n text: 'text-blue-900',\r\n },\r\n Approved: {\r\n border: 'border-green-500',\r\n bg: 'bg-green-50',\r\n text: 'text-green-900',\r\n },\r\n Rejected: {\r\n border: 'border-red-500',\r\n bg: 'bg-red-50',\r\n text: 'text-red-900',\r\n },\r\n Superseded: {\r\n border: 'border-gray-400',\r\n bg: 'bg-gray-50',\r\n text: 'text-gray-700',\r\n },\r\n};\r\n\r\nconst statusIconMap: Record<string, React.ElementType> = {\r\n Proposed: Clock,\r\n Approved: CheckCircle,\r\n Rejected: XCircle,\r\n Superseded: AlertCircle,\r\n};\r\n\r\nexport function TicketSolutionPanel({\r\n ticketId: _ticketId,\r\n solutions,\r\n activeSolution,\r\n mode,\r\n onProposeSolution,\r\n onApproveSolution,\r\n onRejectSolution,\r\n isLoading,\r\n}: TicketSolutionPanelProps): ReactElement {\r\n const { t } = useTranslation('support');\r\n const [proposing, setProposing] = useState(false);\r\n const [proposedContent, setProposedContent] = useState('');\r\n const [rejecting, setRejecting] = useState(false);\r\n const [rejectionReason, setRejectionReason] = useState('');\r\n const [showRejectionModal, setShowRejectionModal] = useState(false);\r\n const [expandedHistory, setExpandedHistory] = useState(false);\r\n\r\n const handleProposeSolution = useCallback(async () => {\r\n if (!proposedContent.trim() || !onProposeSolution) return;\r\n\r\n setProposing(true);\r\n try {\r\n await onProposeSolution(proposedContent);\r\n setProposedContent('');\r\n } finally {\r\n setProposing(false);\r\n }\r\n }, [proposedContent, onProposeSolution]);\r\n\r\n const handleRejectClick = useCallback(() => {\r\n setShowRejectionModal(true);\r\n }, []);\r\n\r\n const handleSubmitRejection = useCallback(async () => {\r\n if (!rejectionReason.trim() || !onRejectSolution) return;\r\n\r\n setRejecting(true);\r\n try {\r\n await onRejectSolution(rejectionReason);\r\n setRejectionReason('');\r\n setShowRejectionModal(false);\r\n } finally {\r\n setRejecting(false);\r\n }\r\n }, [rejectionReason, onRejectSolution]);\r\n\r\n const getAutoCloseCountdown = (solution: TicketSolutionDto): number | null => {\r\n if (!solution.autoCloseAt) return null;\r\n const closeDate = new Date(solution.autoCloseAt);\r\n const now = new Date();\r\n const diffMs = closeDate.getTime() - now.getTime();\r\n const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));\r\n return diffDays > 0 ? diffDays : 0;\r\n };\r\n\r\n if (solutions.length === 0 && mode === 'support' && onProposeSolution) {\r\n return (\r\n <div className=\"bg-[var(--bg-card)] rounded-lg border border-[var(--border-color)] p-4\">\r\n <h3 className=\"text-lg font-semibold mb-4\">{t('solution.proposeSolution', 'Propose Solution')}</h3>\r\n <textarea\r\n value={proposedContent}\r\n onChange={(e) => setProposedContent(e.target.value)}\r\n placeholder={t('solution.enterSolution', 'Enter solution...')}\r\n className=\"w-full p-3 border border-[var(--border-color)] rounded-lg bg-[var(--bg-primary)] text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-500)] resize-none\"\r\n rows={4}\r\n disabled={proposing || isLoading}\r\n />\r\n <div className=\"mt-3 flex gap-2 justify-end\">\r\n <button\r\n onClick={handleProposeSolution}\r\n disabled={!proposedContent.trim() || proposing || isLoading}\r\n className=\"btn btn-primary flex items-center gap-2\"\r\n >\r\n {proposing ? (\r\n <>\r\n <Loader2 className=\"w-4 h-4 animate-spin\" />\r\n {t('solution.proposeSolution', 'Propose Solution')}\r\n </>\r\n ) : (\r\n <>\r\n <Send className=\"w-4 h-4\" />\r\n {t('solution.proposeSolution', 'Propose Solution')}\r\n </>\r\n )}\r\n </button>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"space-y-4\">\r\n {/* Active solution display */}\r\n {activeSolution && (\r\n <div className={`rounded-lg border-2 p-4 ${statusColorMap[activeSolution.status].border} ${statusColorMap[activeSolution.status].bg}`}>\r\n <div className=\"flex items-start justify-between mb-3\">\r\n <div className=\"flex items-center gap-2\">\r\n {(() => {\r\n const StatusIcon = statusIconMap[activeSolution.status];\r\n return <StatusIcon className={`w-5 h-5 ${statusColorMap[activeSolution.status].text}`} />;\r\n })()}\r\n <div>\r\n <h4 className={`font-semibold ${statusColorMap[activeSolution.status].text}`}>\r\n {t(`solution.status.${activeSolution.status.toLowerCase()}`, activeSolution.status)}\r\n </h4>\r\n <p className=\"text-sm opacity-75\">\r\n {t('solution.solutionNumber', 'Solution #')}{activeSolution.solutionNumber}\r\n {' - '}\r\n {t('solution.proposedBy', 'Proposed by')} {activeSolution.proposedByUserName || 'Unknown'}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"bg-white/50 rounded p-3 mb-3 max-h-32 overflow-y-auto\">\r\n <p className=\"text-sm whitespace-pre-wrap\">{activeSolution.content}</p>\r\n </div>\r\n\r\n {/* Auto-close countdown */}\r\n {activeSolution.status === 'Proposed' && activeSolution.autoCloseAt && (\r\n <div className=\"mb-3 p-2 bg-yellow-100/50 border-l-2 border-yellow-500 rounded\">\r\n <p className=\"text-sm\">\r\n {t('solution.autoCloseWarning', 'Will be auto-approved in')}\r\n {' '}\r\n <strong>{getAutoCloseCountdown(activeSolution)} {t('solution.autoCloseCountdown', 'days')}</strong>\r\n {activeSolution.autoCloseWarningSent && (\r\n <>\r\n {' ('}\r\n {t('solution.warningEmailSent', 'warning email sent')}\r\n {')'}\r\n </>\r\n )}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {/* Status-specific info */}\r\n {activeSolution.status === 'Approved' && (\r\n <p className=\"text-sm\">\r\n {t('solution.approvedBy', 'Approved by')} {activeSolution.approvedByUserName || 'Unknown'}\r\n {' on '}\r\n {activeSolution.approvedAt && new Date(activeSolution.approvedAt).toLocaleDateString()}\r\n </p>\r\n )}\r\n {activeSolution.status === 'Rejected' && (\r\n <div>\r\n <p className=\"text-sm mb-2\">\r\n {t('solution.rejectedBy', 'Rejected by')} {activeSolution.rejectedByUserName || 'Unknown'}\r\n {' on '}\r\n {activeSolution.rejectedAt && new Date(activeSolution.rejectedAt).toLocaleDateString()}\r\n </p>\r\n {activeSolution.rejectionReason && (\r\n <p className=\"text-sm bg-white/30 p-2 rounded italic\">\r\n {t('solution.rejectionReason', 'Reason')}: {activeSolution.rejectionReason}\r\n </p>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Client-mode action buttons */}\r\n {mode === 'client' && activeSolution.status === 'Proposed' && (\r\n <div className=\"mt-3 flex gap-2 justify-end\">\r\n <button\r\n onClick={handleRejectClick}\r\n disabled={rejecting || isLoading}\r\n className=\"btn btn-secondary text-sm py-1.5\"\r\n >\r\n {t('solution.rejectSolution', 'Reject')}\r\n </button>\r\n <button\r\n onClick={onApproveSolution}\r\n disabled={rejecting || isLoading || !onApproveSolution}\r\n className=\"btn btn-primary text-sm py-1.5 flex items-center gap-2\"\r\n >\r\n {isLoading ? (\r\n <>\r\n <Loader2 className=\"w-3 h-3 animate-spin\" />\r\n {t('solution.acceptSolution', 'Approve')}\r\n </>\r\n ) : (\r\n <>\r\n <CheckCircle className=\"w-3 h-3\" />\r\n {t('solution.acceptSolution', 'Approve')}\r\n </>\r\n )}\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Support mode: Propose new solution */}\r\n {mode === 'support' && onProposeSolution && !activeSolution && (\r\n <div className=\"bg-[var(--bg-card)] rounded-lg border border-[var(--border-color)] p-4\">\r\n <h4 className=\"font-semibold mb-3\">{t('solution.proposeSolution', 'Propose Solution')}</h4>\r\n <textarea\r\n value={proposedContent}\r\n onChange={(e) => setProposedContent(e.target.value)}\r\n placeholder={t('solution.enterSolution', 'Enter solution...')}\r\n className=\"w-full p-3 border border-[var(--border-color)] rounded-lg bg-[var(--bg-primary)] text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-500)] resize-none\"\r\n rows={4}\r\n disabled={proposing || isLoading}\r\n />\r\n <div className=\"mt-3 flex gap-2 justify-end\">\r\n <button\r\n onClick={handleProposeSolution}\r\n disabled={!proposedContent.trim() || proposing || isLoading}\r\n className=\"btn btn-primary flex items-center gap-2\"\r\n >\r\n {proposing ? (\r\n <>\r\n <Loader2 className=\"w-4 h-4 animate-spin\" />\r\n {t('solution.proposeSolution', 'Propose Solution')}\r\n </>\r\n ) : (\r\n <>\r\n <Send className=\"w-4 h-4\" />\r\n {t('solution.proposeSolution', 'Propose Solution')}\r\n </>\r\n )}\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Solution history (collapsed by default) */}\r\n {solutions.length > 1 && (\r\n <div className=\"bg-[var(--bg-card)] rounded-lg border border-[var(--border-color)] overflow-hidden\">\r\n <button\r\n onClick={() => setExpandedHistory(!expandedHistory)}\r\n className=\"w-full flex items-center gap-2 p-4 hover:bg-[var(--bg-secondary)] transition-colors\"\r\n >\r\n {expandedHistory ? (\r\n <ChevronDown className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n ) : (\r\n <ChevronRight className=\"w-4 h-4 text-[var(--text-tertiary)]\" />\r\n )}\r\n <h4 className=\"font-semibold\">\r\n {t('solution.solutionHistory', 'Solution History')} ({solutions.length})\r\n </h4>\r\n </button>\r\n\r\n {expandedHistory && (\r\n <div className=\"border-t border-[var(--border-color)] p-4 space-y-3\">\r\n {solutions.map((solution) => (\r\n <div\r\n key={solution.id}\r\n className={`rounded-lg border p-3 ${statusColorMap[solution.status].border}`}\r\n >\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <div className=\"flex items-center gap-2\">\r\n {(() => {\r\n const StatusIcon = statusIconMap[solution.status];\r\n return <StatusIcon className={`w-4 h-4 ${statusColorMap[solution.status].text}`} />;\r\n })()}\r\n <span className=\"text-sm font-medium\">\r\n {t(`solution.status.${solution.status.toLowerCase()}`, solution.status)}\r\n </span>\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n #{solution.solutionNumber}\r\n </span>\r\n </div>\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n {new Date(solution.proposedAt).toLocaleDateString()}\r\n </span>\r\n </div>\r\n <p className=\"text-sm text-[var(--text-secondary)] mb-2 line-clamp-2\">\r\n {solution.content}\r\n </p>\r\n <p className=\"text-xs text-[var(--text-tertiary)]\">\r\n {t('solution.proposedBy', 'Proposed by')} {solution.proposedByUserName || 'Unknown'}\r\n </p>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Empty state */}\r\n {solutions.length === 0 && mode === 'client' && (\r\n <div className=\"text-center py-6 text-[var(--text-tertiary)]\">\r\n <p>{t('solution.noSolutions', 'No solutions proposed yet')}</p>\r\n </div>\r\n )}\r\n\r\n {/* Rejection modal */}\r\n {showRejectionModal && (\r\n <div className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50\">\r\n <div className=\"bg-[var(--bg-card)] rounded-lg shadow-lg p-6 w-96 max-w-[90vw]\">\r\n <h3 className=\"text-lg font-semibold mb-4\">\r\n {t('solution.rejectSolution', 'Reject Solution')}\r\n </h3>\r\n <textarea\r\n value={rejectionReason}\r\n onChange={(e) => setRejectionReason(e.target.value)}\r\n placeholder={t('solution.enterRejectReason', 'Enter reason for rejection...')}\r\n className=\"w-full p-3 border border-[var(--border-color)] rounded-lg bg-[var(--bg-primary)] text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-500)] resize-none\"\r\n rows={4}\r\n disabled={rejecting}\r\n />\r\n <div className=\"mt-4 flex gap-2 justify-end\">\r\n <button\r\n onClick={() => setShowRejectionModal(false)}\r\n disabled={rejecting}\r\n className=\"btn btn-secondary\"\r\n >\r\n {t('common:cancel', 'Cancel')}\r\n </button>\r\n <button\r\n onClick={handleSubmitRejection}\r\n disabled={!rejectionReason.trim() || rejecting}\r\n className=\"btn btn-primary flex items-center gap-2\"\r\n >\r\n {rejecting ? (\r\n <>\r\n <Loader2 className=\"w-4 h-4 animate-spin\" />\r\n {t('solution.rejectSolution', 'Reject')}\r\n </>\r\n ) : (\r\n <>\r\n <XCircle className=\"w-4 h-4\" />\r\n {t('solution.rejectSolution', 'Reject')}\r\n </>\r\n )}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n","import { api } from './apiClient';\r\n\r\n// Classification Types\r\nexport interface TicketClassificationDto {\r\n suggestedType: string;\r\n suggestedPriority: string;\r\n suggestedCategory: string | null;\r\n complexity: string;\r\n sentiment: string;\r\n detectedLanguage: string | null;\r\n confidenceScore: number;\r\n suggestedSkills: string[] | null;\r\n classifiedAt: string;\r\n modelUsed: string | null;\r\n}\r\n\r\n// Routing Types\r\nexport interface RoutingAgentSuggestionDto {\r\n userId: string;\r\n userName: string | null;\r\n matchScore: number;\r\n reasoning: string | null;\r\n}\r\n\r\nexport interface TicketRoutingSuggestionDto {\r\n suggestedAgents: RoutingAgentSuggestionDto[];\r\n suggestedGroupId: string | null;\r\n suggestedGroupName: string | null;\r\n priorityAdjustment: string | null;\r\n reasoning: string | null;\r\n confidenceScore: number;\r\n}\r\n\r\n// Summary Types\r\nexport interface TicketSummaryDto {\r\n summary: string;\r\n keyPoints: string[];\r\n sentiment: string;\r\n blockers: string[] | null;\r\n modelUsed: string | null;\r\n}\r\n\r\n// Summary Result Types\r\nexport interface TicketSummaryResultDto {\r\n summary: TicketSummaryDto | null;\r\n isAiAvailable: boolean;\r\n errorCode: string | null;\r\n errorMessage: string | null;\r\n}\r\n\r\n// Analysis Types\r\nexport interface TicketAiAnalysisDto {\r\n classification: TicketClassificationDto | null;\r\n routingSuggestion: TicketRoutingSuggestionDto | null;\r\n isAiAvailable: boolean;\r\n errorCode: string | null;\r\n errorMessage: string | null;\r\n}\r\n\r\n// Cached Data Types\r\nexport interface TicketAiCachedDataDto {\r\n classification: TicketClassificationDto | null;\r\n routingSuggestion: TicketRoutingSuggestionDto | null;\r\n routingSuggestionGeneratedAt: string | null;\r\n summary: TicketSummaryDto | null;\r\n summaryGeneratedAt: string | null;\r\n isAiAvailable: boolean;\r\n}\r\n\r\n// Status Types\r\nexport interface AiRoutingStatusDto {\r\n isAvailable: boolean;\r\n isOverBudget: boolean;\r\n providerName: string | null;\r\n modelName: string | null;\r\n errorCode: string | null;\r\n}\r\n\r\n// Request Types\r\nexport interface ApplyAiRoutingSuggestionRequest {\r\n assignToUserId?: string;\r\n assignToGroupId?: string;\r\n applyPriorityAdjustment?: boolean;\r\n}\r\n\r\n// Cached Data API\r\nexport const cachedAiApi = {\r\n getCached: (ticketId: string) =>\r\n api.get<TicketAiCachedDataDto>(`/api/support/ai-routing/cached/${ticketId}`),\r\n};\r\n\r\n// Classification API\r\nexport const classifyApi = {\r\n classify: (ticketId: string) =>\r\n api.post<TicketClassificationDto>(\r\n `/api/support/ai-routing/classify/${ticketId}`\r\n ),\r\n};\r\n\r\n// Analysis API\r\nexport const analyzeApi = {\r\n analyze: (ticketId: string) =>\r\n api.get<TicketAiAnalysisDto>(`/api/support/ai-routing/analyze/${ticketId}`),\r\n};\r\n\r\n// Summarization API\r\nexport const summarizeApi = {\r\n summarize: (ticketId: string) =>\r\n api.post<TicketSummaryResultDto>(\r\n `/api/support/ai-routing/summarize/${ticketId}`\r\n ),\r\n};\r\n\r\n// Routing Decision API\r\nexport const routingApi = {\r\n applyRouting: (ticketId: string, data: ApplyAiRoutingSuggestionRequest) =>\r\n api.post<void>(`/api/support/ai-routing/apply/${ticketId}`, data),\r\n};\r\n\r\n// Status API\r\nexport const aiRoutingStatusApi = {\r\n getStatus: () =>\r\n api.get<AiRoutingStatusDto>('/api/support/ai-routing/status'),\r\n};\r\n","import type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\n\r\ninterface AiClassificationBadgeProps {\r\n readonly classification: {\r\n readonly suggestedType: string;\r\n readonly suggestedPriority: string;\r\n readonly confidenceScore: number;\r\n readonly sentiment: string;\r\n } | null;\r\n}\r\n\r\nexport function AiClassificationBadge({ classification }: AiClassificationBadgeProps): ReactElement | null {\r\n const { t } = useTranslation('support');\r\n\r\n if (!classification) {\r\n return null;\r\n }\r\n\r\n const getConfidenceColor = (score: number): string => {\r\n if (score > 0.8) return 'bg-[var(--success-bg)] text-[var(--success-text)]';\r\n if (score > 0.5) return 'bg-[var(--warning-bg)] text-[var(--warning-text)]';\r\n return 'bg-[var(--error-bg)] text-[var(--error-text)]';\r\n };\r\n\r\n const getConfidenceLabel = (score: number): string => {\r\n if (score > 0.8) return t('aiClassification.confidence.high');\r\n if (score > 0.5) return t('aiClassification.confidence.medium');\r\n return t('aiClassification.confidence.low');\r\n };\r\n\r\n return (\r\n <div className=\"flex items-center gap-2\">\r\n <span className=\"px-3 py-1 rounded-full text-xs font-medium bg-[var(--info-bg)] text-[var(--info-text)]\">\r\n {t(`tickets.types.${classification.suggestedType}`)}\r\n </span>\r\n <span className=\"px-3 py-1 rounded-full text-xs font-medium bg-[var(--warning-bg)] text-[var(--warning-text)]\">\r\n {t(`tickets.priorities.${classification.suggestedPriority}`)}\r\n </span>\r\n <span\r\n className={`px-3 py-1 rounded-full text-xs font-medium ${getConfidenceColor(\r\n classification.confidenceScore\r\n )}`}\r\n >\r\n {getConfidenceLabel(classification.confidenceScore)} ({Math.round(\r\n classification.confidenceScore * 100\r\n )}%)\r\n </span>\r\n </div>\r\n );\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from 'react';\r\n\r\ninterface UseAiOperationOptions<TResult> {\r\n initialData?: TResult | null;\r\n}\r\n\r\ninterface UseAiOperationResult<TResult> {\r\n data: TResult | null;\r\n loading: boolean;\r\n error: string | null;\r\n execute: (executeFn: (signal: AbortSignal) => Promise<TResult>) => Promise<void>;\r\n cancel: () => void;\r\n reset: () => void;\r\n}\r\n\r\nexport function useAiOperation<TResult>(options?: UseAiOperationOptions<TResult>): UseAiOperationResult<TResult> {\r\n const [data, setData] = useState<TResult | null>(options?.initialData ?? null);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const abortRef = useRef<AbortController | null>(null);\r\n const cancelledRef = useRef(false);\r\n\r\n // Track unmount to prevent setState on unmounted component\r\n useEffect(() => {\r\n cancelledRef.current = false;\r\n return () => {\r\n cancelledRef.current = true;\r\n abortRef.current?.abort();\r\n };\r\n }, []);\r\n\r\n const execute = useCallback(\r\n async (executeFn: (signal: AbortSignal) => Promise<TResult>) => {\r\n abortRef.current?.abort();\r\n const controller = new AbortController();\r\n abortRef.current = controller;\r\n\r\n setLoading(true);\r\n setError(null);\r\n\r\n try {\r\n const result = await executeFn(controller.signal);\r\n if (!cancelledRef.current && !controller.signal.aborted) {\r\n setData(result);\r\n }\r\n } catch (err) {\r\n if (!cancelledRef.current && !controller.signal.aborted) {\r\n setError(err instanceof Error ? err.message : 'Unknown error');\r\n }\r\n } finally {\r\n if (!cancelledRef.current && !controller.signal.aborted) {\r\n setLoading(false);\r\n }\r\n }\r\n },\r\n []\r\n );\r\n\r\n const cancel = useCallback(() => {\r\n abortRef.current?.abort();\r\n if (!cancelledRef.current) {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n const reset = useCallback(() => {\r\n setData(null);\r\n setError(null);\r\n setLoading(false);\r\n }, []);\r\n\r\n return { data, loading, error, execute, cancel, reset };\r\n}\r\n","import { useCallback, useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Loader2, Check, AlertCircle, Sparkles, RefreshCw } from 'lucide-react';\r\nimport { analyzeApi, routingApi, type TicketAiAnalysisDto, type ApplyAiRoutingSuggestionRequest, type TicketClassificationDto, type TicketRoutingSuggestionDto } from '@/services/api/aiRoutingApi';\r\nimport { AiClassificationBadge } from './AiClassificationBadge';\r\nimport { useAiOperation } from '@/hooks/useAiOperation';\r\n\r\ninterface AiRoutingSuggestionPanelProps {\r\n readonly ticketId: string;\r\n readonly cachedClassification?: TicketClassificationDto | null;\r\n readonly cachedRouting?: TicketRoutingSuggestionDto | null;\r\n readonly cachedGeneratedAt?: string | null;\r\n readonly processing?: boolean;\r\n readonly onProcessingStart?: () => void;\r\n readonly onProcessingEnd?: () => void;\r\n}\r\n\r\nexport function AiRoutingSuggestionPanel({ ticketId, cachedClassification, cachedRouting, cachedGeneratedAt, processing, onProcessingStart, onProcessingEnd }: AiRoutingSuggestionPanelProps): ReactElement {\r\n const { t } = useTranslation('support');\r\n\r\n const initialData = (cachedClassification || cachedRouting) ? {\r\n classification: cachedClassification ?? null,\r\n routingSuggestion: cachedRouting ?? null,\r\n isAiAvailable: true,\r\n errorCode: null,\r\n errorMessage: null,\r\n } as TicketAiAnalysisDto : undefined;\r\n\r\n const { data: analysis, loading, error, execute: executeAnalysis, reset: resetAnalysis } = useAiOperation<TicketAiAnalysisDto>({ initialData });\r\n const [applying, setApplying] = useState(false);\r\n\r\n const loadAnalysis = useCallback(async () => {\r\n onProcessingStart?.();\r\n await executeAnalysis(async () => {\r\n const data = await analyzeApi.analyze(ticketId);\r\n onProcessingEnd?.();\r\n return data;\r\n });\r\n }, [ticketId, executeAnalysis, onProcessingStart, onProcessingEnd]);\r\n\r\n const handleApply = useCallback(async () => {\r\n if (!analysis?.routingSuggestion) return;\r\n\r\n try {\r\n setApplying(true);\r\n const request: ApplyAiRoutingSuggestionRequest = {\r\n assignToUserId: analysis.routingSuggestion.suggestedAgents[0]?.userId,\r\n assignToGroupId: analysis.routingSuggestion.suggestedGroupId ?? undefined,\r\n applyPriorityAdjustment: analysis.routingSuggestion.priorityAdjustment !== null,\r\n };\r\n await routingApi.applyRouting(ticketId, request);\r\n resetAnalysis();\r\n } catch {\r\n // Error is handled silently, could be enhanced with toast notification\r\n } finally {\r\n setApplying(false);\r\n }\r\n }, [analysis, resetAnalysis, t]);\r\n\r\n // Error state with retry\r\n if (error && !analysis) {\r\n return (\r\n <div className=\"card p-4 bg-[var(--error-bg)]/10 border border-[var(--error-border)]\">\r\n <div className=\"flex items-start gap-3\">\r\n <AlertCircle className=\"w-5 h-5 text-[var(--error-text)] flex-shrink-0 mt-0.5\" />\r\n <div className=\"flex-1\">\r\n <p className=\"text-sm text-[var(--error-text)] font-medium mb-1\">\r\n {t('aiRouting.error')}\r\n </p>\r\n <p className=\"text-xs text-[var(--text-secondary)]\">{error}</p>\r\n <button\r\n onClick={loadAnalysis}\r\n disabled={loading}\r\n className=\"btn btn-secondary btn-sm mt-2 flex items-center gap-1\"\r\n >\r\n <RefreshCw className=\"w-3 h-3\" />\r\n {t('common:actions.retry')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n // No cached data and not yet analyzed\r\n if (!analysis) {\r\n // Processing in background (user navigated away and came back)\r\n if (processing) {\r\n return (\r\n <div className=\"card p-4 bg-[var(--info-bg)]/10 border border-[var(--info-border)]\">\r\n <div className=\"flex items-center gap-3\">\r\n <Loader2 className=\"w-5 h-5 animate-spin text-[var(--info-text)] flex-shrink-0\" />\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {t('aiRouting.processing')}\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n }\r\n return (\r\n <button\r\n onClick={loadAnalysis}\r\n disabled={loading}\r\n className=\"btn btn-secondary btn-sm flex items-center gap-2\"\r\n >\r\n {loading ? (\r\n <Loader2 className=\"w-4 h-4 animate-spin\" />\r\n ) : (\r\n <Sparkles className=\"w-4 h-4\" />\r\n )}\r\n {t('aiRouting.analyze')}\r\n </button>\r\n );\r\n }\r\n\r\n if (loading && !analysis) {\r\n return (\r\n <div className=\"card p-6 flex items-center justify-center\">\r\n <Loader2 className=\"w-6 h-6 animate-spin text-[var(--color-primary-600)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n // AI not available (prompts not configured)\r\n if (!analysis.isAiAvailable) {\r\n const errorKey = analysis.errorCode ? `aiRouting.errors.${analysis.errorCode}` : null;\r\n const message = (errorKey && t(errorKey) !== errorKey) ? t(errorKey) : (analysis.errorMessage ?? t('aiRouting.notConfigured'));\r\n return (\r\n <div className=\"card p-4 bg-[var(--warning-bg)]/10 border border-[var(--warning-border)]\">\r\n <div className=\"flex items-start gap-3\">\r\n <AlertCircle className=\"w-5 h-5 text-[var(--warning-text)] flex-shrink-0 mt-0.5\" />\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {message}\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n // AI available but both classification and routing failed\r\n if (!analysis.classification && !analysis.routingSuggestion) {\r\n return (\r\n <div className=\"card p-4 bg-[var(--error-bg)]/10 border border-[var(--error-border)]\">\r\n <div className=\"flex items-start gap-3\">\r\n <AlertCircle className=\"w-5 h-5 text-[var(--error-text)] flex-shrink-0 mt-0.5\" />\r\n <div className=\"flex-1\">\r\n <p className=\"text-sm text-[var(--error-text)] font-medium mb-1\">\r\n {t('aiRouting.error')}\r\n </p>\r\n <p className=\"text-xs text-[var(--text-secondary)]\">\r\n {analysis.errorMessage ?? t('aiRouting.notAvailable')}\r\n </p>\r\n <button\r\n onClick={loadAnalysis}\r\n disabled={loading}\r\n className=\"btn btn-secondary btn-sm mt-2 flex items-center gap-1\"\r\n >\r\n <RefreshCw className=\"w-3 h-3\" />\r\n {t('common:actions.retry')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n const routing = analysis?.routingSuggestion;\r\n const generatedAt = cachedGeneratedAt && !loading ? new Date(cachedGeneratedAt) : null;\r\n\r\n return (\r\n <div className=\"card p-6 bg-[var(--info-bg)]/10 border border-[var(--info-border)]\">\r\n <div className=\"space-y-4\">\r\n {/* Partial failure warning */}\r\n {analysis?.errorMessage && (\r\n <div className=\"flex items-start gap-2 text-xs text-[var(--warning-text)] bg-[var(--warning-bg)]/20 rounded p-2\">\r\n <AlertCircle className=\"w-4 h-4 flex-shrink-0 mt-0.5\" />\r\n <p>{analysis.errorMessage}</p>\r\n </div>\r\n )}\r\n\r\n <div className=\"flex items-start gap-3\">\r\n <Sparkles className=\"w-5 h-5 text-[var(--info-text)] flex-shrink-0 mt-1\" />\r\n <div className=\"w-full\">\r\n <div className=\"flex items-center justify-between mb-3\">\r\n <h3 className=\"font-semibold text-sm\">\r\n {t('aiRouting.suggestedRouting')}\r\n </h3>\r\n <button\r\n onClick={loadAnalysis}\r\n disabled={loading}\r\n className=\"btn btn-secondary btn-sm flex items-center gap-1\"\r\n title={t('aiRouting.refresh')}\r\n >\r\n {loading ? (\r\n <Loader2 className=\"w-3 h-3 animate-spin\" />\r\n ) : (\r\n <RefreshCw className=\"w-3 h-3\" />\r\n )}\r\n {t('aiRouting.refresh')}\r\n </button>\r\n </div>\r\n\r\n {generatedAt && (\r\n <p className=\"text-xs text-[var(--text-tertiary)] mb-3\">\r\n {t('aiRouting.generatedAt', { date: generatedAt.toLocaleString() })}\r\n </p>\r\n )}\r\n\r\n <div className=\"space-y-3\">\r\n {analysis?.classification && (\r\n <AiClassificationBadge\r\n classification={analysis.classification}\r\n />\r\n )}\r\n\r\n {(routing?.suggestedAgents?.length ?? 0) > 0 && (\r\n <div className=\"text-sm\">\r\n <p className=\"text-[var(--text-secondary)] mb-2\">\r\n {t('aiRouting.suggestedAgents')}\r\n </p>\r\n <div className=\"space-y-1\">\r\n {routing?.suggestedAgents?.map((agent) => (\r\n <div key={agent.userId} className=\"flex items-center justify-between\">\r\n <p className=\"font-medium\">{agent.userName || agent.userId}</p>\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n {Math.round(agent.matchScore * 100)}%\r\n </span>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {routing?.suggestedGroupName && (\r\n <div className=\"text-sm\">\r\n <p className=\"text-[var(--text-secondary)] mb-1\">\r\n {t('aiRouting.suggestedGroup')}\r\n </p>\r\n <p className=\"font-medium\">{routing.suggestedGroupName}</p>\r\n </div>\r\n )}\r\n\r\n {routing?.reasoning && (\r\n <div className=\"text-sm\">\r\n <p className=\"text-[var(--text-secondary)] mb-1\">\r\n {t('aiRouting.reasoning')}\r\n </p>\r\n <p className=\"italic\">{routing.reasoning}</p>\r\n </div>\r\n )}\r\n\r\n <div className=\"pt-3 border-t border-[var(--border-color)] flex gap-2\">\r\n {routing && (\r\n <button\r\n onClick={handleApply}\r\n disabled={applying}\r\n className=\"btn btn-primary btn-sm flex items-center gap-2 flex-1 justify-center\"\r\n >\r\n {applying ? (\r\n <Loader2 className=\"w-4 h-4 animate-spin\" />\r\n ) : (\r\n <Check className=\"w-4 h-4\" />\r\n )}\r\n {t('aiRouting.apply')}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useCallback } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Loader2, Zap, AlertCircle, RefreshCw } from 'lucide-react';\r\nimport { summarizeApi, type TicketSummaryResultDto, type TicketSummaryDto } from '@/services/api/aiRoutingApi';\r\nimport { useAiOperation } from '@/hooks/useAiOperation';\r\n\r\ninterface AiTicketSummaryProps {\r\n readonly ticketId: string;\r\n readonly cachedSummary?: TicketSummaryDto | null;\r\n readonly cachedGeneratedAt?: string | null;\r\n readonly processing?: boolean;\r\n readonly onProcessingStart?: () => void;\r\n readonly onProcessingEnd?: () => void;\r\n}\r\n\r\nexport function AiTicketSummary({ ticketId, cachedSummary, cachedGeneratedAt, processing, onProcessingStart, onProcessingEnd }: AiTicketSummaryProps): ReactElement {\r\n const { t } = useTranslation('support');\r\n\r\n const initialData = cachedSummary ? {\r\n summary: cachedSummary,\r\n isAiAvailable: true,\r\n errorCode: null,\r\n errorMessage: null,\r\n } as TicketSummaryResultDto : undefined;\r\n\r\n const { data: result, loading, error, execute: executeGenerate, reset: _resetResult } = useAiOperation<TicketSummaryResultDto>({ initialData });\r\n\r\n const generateSummary = useCallback(async () => {\r\n onProcessingStart?.();\r\n await executeGenerate(async () => {\r\n const data = await summarizeApi.summarize(ticketId);\r\n onProcessingEnd?.();\r\n return data;\r\n });\r\n }, [ticketId, executeGenerate, onProcessingStart, onProcessingEnd]);\r\n\r\n // Network error state with retry\r\n if (error && !result) {\r\n return (\r\n <div className=\"card p-4 bg-[var(--error-bg)]/10 border border-[var(--error-border)]\">\r\n <div className=\"flex items-start gap-3\">\r\n <AlertCircle className=\"w-5 h-5 text-[var(--error-text)] flex-shrink-0 mt-0.5\" />\r\n <div className=\"flex-1\">\r\n <p className=\"text-sm text-[var(--error-text)] font-medium mb-1\">\r\n {t('aiSummary.error')}\r\n </p>\r\n <p className=\"text-xs text-[var(--text-secondary)]\">{error}</p>\r\n <button\r\n onClick={generateSummary}\r\n disabled={loading}\r\n className=\"btn btn-secondary btn-sm mt-2 flex items-center gap-1\"\r\n >\r\n <RefreshCw className=\"w-3 h-3\" />\r\n {t('common:actions.retry')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n // No cached data and not yet generated\r\n if (!result) {\r\n // Processing in background (user navigated away and came back)\r\n if (processing) {\r\n return (\r\n <div className=\"card p-4 bg-[var(--info-bg)]/10 border border-[var(--info-border)]\">\r\n <div className=\"flex items-center gap-3\">\r\n <Loader2 className=\"w-5 h-5 animate-spin text-[var(--info-text)] flex-shrink-0\" />\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {t('aiSummary.processing')}\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n }\r\n return (\r\n <button\r\n onClick={generateSummary}\r\n disabled={loading}\r\n className=\"btn btn-secondary btn-sm flex items-center gap-2\"\r\n >\r\n {loading ? (\r\n <Loader2 className=\"w-4 h-4 animate-spin\" />\r\n ) : (\r\n <Zap className=\"w-4 h-4\" />\r\n )}\r\n {t('aiSummary.generate')}\r\n </button>\r\n );\r\n }\r\n\r\n if (loading && !result) {\r\n return (\r\n <div className=\"card p-6 flex items-center justify-center\">\r\n <Loader2 className=\"w-6 h-6 animate-spin text-[var(--color-primary-600)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n // AI not available (prompts not configured)\r\n if (!result.isAiAvailable) {\r\n const errorKey = result.errorCode ? `aiRouting.errors.${result.errorCode}` : null;\r\n const message = (errorKey && t(errorKey) !== errorKey) ? t(errorKey) : (result.errorMessage ?? t('aiRouting.notConfigured'));\r\n return (\r\n <div className=\"card p-4 bg-[var(--warning-bg)]/10 border border-[var(--warning-border)]\">\r\n <div className=\"flex items-start gap-3\">\r\n <AlertCircle className=\"w-5 h-5 text-[var(--warning-text)] flex-shrink-0 mt-0.5\" />\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {message}\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n // AI available but summarization failed\r\n if (!result.summary) {\r\n const errorKey = result.errorCode ? `aiSummary.errors.${result.errorCode}` : null;\r\n const message = (errorKey && t(errorKey) !== errorKey) ? t(errorKey) : (result.errorMessage ?? t('aiSummary.error'));\r\n return (\r\n <div className=\"card p-4 bg-[var(--error-bg)]/10 border border-[var(--error-border)]\">\r\n <div className=\"flex items-start gap-3\">\r\n <AlertCircle className=\"w-5 h-5 text-[var(--error-text)] flex-shrink-0 mt-0.5\" />\r\n <div className=\"flex-1\">\r\n <p className=\"text-sm text-[var(--error-text)] font-medium mb-1\">\r\n {t('aiSummary.error')}\r\n </p>\r\n <p className=\"text-xs text-[var(--text-secondary)]\">{message}</p>\r\n <button\r\n onClick={generateSummary}\r\n disabled={loading}\r\n className=\"btn btn-secondary btn-sm mt-2 flex items-center gap-1\"\r\n >\r\n <RefreshCw className=\"w-3 h-3\" />\r\n {t('common:actions.retry')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n const summary = result.summary;\r\n const generatedAt = cachedGeneratedAt && !loading ? new Date(cachedGeneratedAt) : null;\r\n\r\n return (\r\n <div className=\"card p-6 bg-[var(--bg-secondary)]\">\r\n <div className=\"flex items-center justify-between mb-4\">\r\n <h3 className=\"font-semibold flex items-center gap-2\">\r\n <Zap className=\"w-5 h-5\" />\r\n {t('aiSummary.title')}\r\n </h3>\r\n <button\r\n onClick={generateSummary}\r\n disabled={loading}\r\n className=\"btn btn-secondary btn-sm flex items-center gap-1\"\r\n title={t('aiSummary.refresh')}\r\n >\r\n {loading ? (\r\n <Loader2 className=\"w-3 h-3 animate-spin\" />\r\n ) : (\r\n <RefreshCw className=\"w-3 h-3\" />\r\n )}\r\n {t('aiSummary.refresh')}\r\n </button>\r\n </div>\r\n\r\n {generatedAt && (\r\n <p className=\"text-xs text-[var(--text-tertiary)] mb-3\">\r\n {t('aiSummary.generatedAt', { date: generatedAt.toLocaleString() })}\r\n </p>\r\n )}\r\n\r\n <div className=\"space-y-4\">\r\n <div>\r\n <p className=\"text-sm text-[var(--text-secondary)] mb-2 font-medium\">\r\n {t('aiSummary.summary')}\r\n </p>\r\n <p className=\"text-sm\">{summary.summary}</p>\r\n </div>\r\n\r\n {summary.keyPoints.length > 0 && (\r\n <div>\r\n <p className=\"text-sm text-[var(--text-secondary)] mb-2 font-medium\">\r\n {t('aiSummary.keyPoints')}\r\n </p>\r\n <ul className=\"list-disc list-inside space-y-1\">\r\n {summary.keyPoints.map((point, index) => (\r\n <li key={`point-${index}`} className=\"text-sm\">{point}</li>\r\n ))}\r\n </ul>\r\n </div>\r\n )}\r\n\r\n {summary.sentiment && (\r\n <div>\r\n <p className=\"text-sm text-[var(--text-secondary)] mb-2 font-medium\">\r\n {t('aiSummary.sentiment')}\r\n </p>\r\n <p className=\"text-sm\">{summary.sentiment}</p>\r\n </div>\r\n )}\r\n\r\n {(summary.blockers?.length ?? 0) > 0 && (\r\n <div>\r\n <p className=\"text-sm text-[var(--text-secondary)] mb-2 font-medium\">\r\n {t('aiSummary.blockers')}\r\n </p>\r\n <ul className=\"space-y-2\">\r\n {summary.blockers?.map((blocker, index) => (\r\n <li key={`blocker-${index}`} className=\"flex items-start gap-2\">\r\n <span className=\"text-xs px-2 py-1 rounded bg-[var(--warning-bg)] text-[var(--warning-text)] flex-shrink-0 mt-0.5\">\r\n {index + 1}\r\n </span>\r\n <span className=\"text-sm\">{blocker}</span>\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { api } from './apiClient';\r\nimport type { PaginatedResult } from '../../types/pagination';\r\n\r\nexport interface TicketAssigneeDto {\r\n userId: string;\r\n email: string;\r\n fullName: string;\r\n isPrimary: boolean;\r\n assignedViaGroupId?: string;\r\n assignedViaGroupName?: string;\r\n assignedAt?: string;\r\n}\r\n\r\nexport interface TicketListDto {\r\n id: string;\r\n ticketNumber: string;\r\n title: string;\r\n type: string;\r\n status: string;\r\n priority: string;\r\n createdByUserId: string;\r\n createdByName?: string;\r\n assignedToUserId?: string;\r\n assignedToName?: string;\r\n commentCount: number;\r\n assigneeCount: number;\r\n createdAt: string;\r\n updatedAt?: string;\r\n slaResponseDueAt?: string;\r\n slaResolutionDueAt?: string;\r\n slaResponseBreached?: boolean;\r\n slaResolutionBreached?: boolean;\r\n escalationLevel?: string;\r\n tenantId?: string;\r\n tenantName?: string;\r\n tenantSlug?: string;\r\n externalProvider?: string;\r\n externalId?: string;\r\n externalUrl?: string;\r\n isExternallyManaged?: boolean;\r\n}\r\n\r\nexport type SolutionStatus = 'Proposed' | 'Approved' | 'Rejected' | 'Superseded';\r\n\r\nexport interface TicketSolutionDto {\r\n id: string;\r\n ticketId: string;\r\n content: string;\r\n proposedByUserId: string;\r\n proposedByUserName?: string;\r\n proposedAt: string;\r\n status: SolutionStatus;\r\n approvedAt?: string;\r\n approvedByUserId?: string;\r\n approvedByUserName?: string;\r\n rejectedAt?: string;\r\n rejectedByUserId?: string;\r\n rejectedByUserName?: string;\r\n rejectionReason?: string;\r\n autoCloseAt?: string;\r\n autoCloseWarningSent: boolean;\r\n isAutoClosedResult: boolean;\r\n solutionNumber: number;\r\n}\r\n\r\nexport interface TicketDetailDto {\r\n id: string;\r\n ticketNumber: string;\r\n title: string;\r\n description: string;\r\n type: string;\r\n status: string;\r\n priority: string;\r\n createdBy?: UserInfoDto;\r\n assignedTo?: UserInfoDto;\r\n resolution?: string;\r\n resolvedAt?: string;\r\n closedAt?: string;\r\n comments: CommentDto[];\r\n attachments: AttachmentDto[];\r\n assignees?: TicketAssigneeDto[];\r\n solutions?: TicketSolutionDto[];\r\n activeSolution?: TicketSolutionDto;\r\n hasPendingSolution?: boolean;\r\n createdAt: string;\r\n updatedAt?: string;\r\n clientContext?: ClientContextDto;\r\n tenantId?: string;\r\n tenantName?: string;\r\n tenantSlug?: string;\r\n externalProvider?: string;\r\n externalId?: string;\r\n externalUrl?: string;\r\n isExternallyManaged?: boolean;\r\n}\r\n\r\nexport interface TicketingProviderInfoDto {\r\n provider: string;\r\n isConfigured: boolean;\r\n glpiBaseUrl?: string;\r\n}\r\n\r\nexport interface UserInfoDto {\r\n id: string;\r\n email: string;\r\n fullName: string;\r\n}\r\n\r\nexport interface CommentDto {\r\n id: string;\r\n userId: string;\r\n userName: string;\r\n content: string;\r\n isInternal: boolean;\r\n isEdited: boolean;\r\n createdAt: string;\r\n updatedAt?: string;\r\n}\r\n\r\nexport interface AttachmentDto {\r\n id: string;\r\n fileName: string;\r\n contentType: string;\r\n fileSize: number;\r\n createdAt: string;\r\n}\r\n\r\nexport interface TicketActivityDto {\r\n id: string;\r\n action: TicketActivityAction;\r\n fieldChanged?: string;\r\n oldValue?: string;\r\n newValue?: string;\r\n changedByUserId?: string;\r\n changedByUserName?: string;\r\n createdAt: string;\r\n}\r\n\r\nexport type TicketActivityAction =\r\n | 'Created'\r\n | 'StatusChanged'\r\n | 'PriorityChanged'\r\n | 'Assigned'\r\n | 'Unassigned'\r\n | 'Commented'\r\n | 'InternalCommented'\r\n | 'AttachmentAdded'\r\n | 'AttachmentRemoved'\r\n | 'Resolved'\r\n | 'Closed'\r\n | 'Reopened'\r\n | 'Rejected'\r\n | 'SlaResponseBreached'\r\n | 'SlaResolutionBreached'\r\n | 'Escalated'\r\n | 'TitleChanged'\r\n | 'DescriptionChanged'\r\n | 'TypeChanged'\r\n | 'SatisfactionSubmitted';\r\n\r\nexport interface TicketStatsDto {\r\n total: number;\r\n open: number;\r\n inProgress: number;\r\n onHold: number;\r\n resolved: number;\r\n closed: number;\r\n critical: number;\r\n highPriority: number;\r\n}\r\n\r\nexport interface GlpiSyncStatsDto {\r\n syncEnabled: boolean;\r\n syncIntervalMinutes: number;\r\n lastSyncAt: string | null;\r\n lastSyncError: string | null;\r\n totalMappedTickets: number;\r\n ticketsWithSyncErrors: number;\r\n ticketsSyncedOk: number;\r\n successRate: number;\r\n}\r\n\r\n\r\n// Client context types for debugging and support\r\nexport interface ClientContextDto {\r\n sourceUrl?: string;\r\n browser?: BrowserInfoDto;\r\n os?: OsInfoDto;\r\n device?: DeviceInfoDto;\r\n recentErrors?: ErrorInfoDto[];\r\n screenshotAttachmentId?: string;\r\n}\r\n\r\nexport interface BrowserInfoDto {\r\n name?: string;\r\n version?: string;\r\n language?: string;\r\n}\r\n\r\nexport interface OsInfoDto {\r\n name?: string;\r\n version?: string;\r\n}\r\n\r\nexport interface DeviceInfoDto {\r\n type?: string;\r\n screenResolution?: string;\r\n}\r\n\r\nexport interface ErrorInfoDto {\r\n message?: string;\r\n stack?: string;\r\n component?: string;\r\n timestamp?: string;\r\n}\r\n\r\nexport interface CreateTicketRequest {\r\n title: string;\r\n description: string;\r\n type: TicketType;\r\n priority: TicketPriority;\r\n createdByUserId?: string;\r\n assignedToUserId?: string;\r\n clientContext?: ClientContextDto;\r\n}\r\n\r\nexport interface UpdateTicketRequest {\r\n title: string;\r\n description: string;\r\n priority: TicketPriority;\r\n}\r\n\r\nexport interface AddCommentRequest {\r\n content: string;\r\n isInternal?: boolean;\r\n}\r\n\r\nexport type TicketType = 'Bug' | 'FeatureRequest' | 'Suggestion' | 'ApplicationRequest' | 'Support' | 'Question';\r\nexport type TicketStatus = 'Open' | 'InProgress' | 'OnHold' | 'Resolved' | 'Closed' | 'Rejected';\r\nexport type TicketPriority = 'Low' | 'Medium' | 'High' | 'Critical';\r\n\r\nexport interface GetTicketsParams {\r\n page?: number;\r\n pageSize?: number;\r\n search?: string;\r\n status?: TicketStatus;\r\n type?: TicketType;\r\n priority?: TicketPriority;\r\n assignedToUserId?: string;\r\n tenantId?: string;\r\n}\r\n\r\nexport const supportApi = {\r\n tickets: {\r\n getAll: (params?: GetTicketsParams) =>\r\n api.get<PaginatedResult<TicketListDto>>('/api/support/tickets', { params }),\r\n\r\n getMyTickets: (params?: GetTicketsParams) =>\r\n api.get<PaginatedResult<TicketListDto>>('/api/support/tickets/my-assigned', { params }),\r\n\r\n getById: (id: string) =>\r\n api.get<TicketDetailDto>(`/api/support/tickets/${id}`),\r\n\r\n create: (data: CreateTicketRequest) =>\r\n api.post<TicketDetailDto>('/api/support/tickets', data),\r\n\r\n update: (id: string, data: UpdateTicketRequest) =>\r\n api.put<TicketDetailDto>(`/api/support/tickets/${id}`, data),\r\n\r\n updateStatus: (id: string, status: TicketStatus) =>\r\n api.patch(`/api/support/tickets/${id}/status`, { status }),\r\n\r\n assign: (id: string, assignedToUserId: string | null, userIds?: string[], groupIds?: string[]) =>\r\n api.patch(`/api/support/tickets/${id}/assign`, { assignedToUserId, userIds, groupIds }),\r\n\r\n resolve: (id: string, resolution: string) =>\r\n api.patch(`/api/support/tickets/${id}/resolve`, { resolution }),\r\n\r\n close: (id: string) =>\r\n api.patch(`/api/support/tickets/${id}/close`),\r\n\r\n reopen: (id: string) =>\r\n api.patch(`/api/support/tickets/${id}/reopen`),\r\n\r\n addComment: (id: string, data: AddCommentRequest) =>\r\n api.post<CommentDto>(`/api/support/tickets/${id}/comments`, data),\r\n\r\n delete: (id: string) =>\r\n api.delete(`/api/support/tickets/${id}`),\r\n\r\n getStats: (tenantId?: string) =>\r\n api.get<TicketStatsDto>('/api/support/tickets/stats', { params: { tenantId } }),\r\n\r\n getMyAssignedStats: () =>\r\n api.get<TicketStatsDto>('/api/support/tickets/my-assigned/stats'),\r\n\r\n getAttachmentUrl: (ticketId: string, attachmentId: string) =>\r\n `/api/support/tickets/${ticketId}/attachments/${attachmentId}`,\r\n\r\n downloadAttachment: (ticketId: string, attachmentId: string) =>\r\n api.get<Blob>(`/api/support/tickets/${ticketId}/attachments/${attachmentId}`, {\r\n responseType: 'blob',\r\n }),\r\n\r\n uploadAttachment: (ticketId: string, file: File) => {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n return api.post<AttachmentDto>(`/api/support/tickets/${ticketId}/attachments`, formData, {\r\n headers: { 'Content-Type': 'multipart/form-data' },\r\n });\r\n },\r\n\r\n getActivities: (ticketId: string, limit: number = 10) =>\r\n api.get<TicketActivityDto[]>(`/api/support/tickets/${ticketId}/activities`, { params: { limit } }),\r\n\r\n getTicketingProvider: () =>\r\n api.get<TicketingProviderInfoDto>('/api/support/tickets/provider'),\r\n\r\n getGlpiSyncStats: () =>\r\n api.get<GlpiSyncStatsDto | null>('/api/support/tickets/glpi-sync-stats'),\r\n\r\n proposeSolution: (ticketId: string, content: string) =>\r\n api.post<TicketSolutionDto>(`/api/support/tickets/${ticketId}/solutions`, { content }),\r\n\r\n getSolutions: (ticketId: string) =>\r\n api.get<TicketSolutionDto[]>(`/api/support/tickets/${ticketId}/solutions`),\r\n\r\n approveSolution: (ticketId: string) =>\r\n api.patch<TicketSolutionDto>(`/api/support/tickets/${ticketId}/solutions/approve`),\r\n\r\n rejectSolution: (ticketId: string, reason: string) =>\r\n api.patch<TicketSolutionDto>(`/api/support/tickets/${ticketId}/solutions/reject`, { reason }),\r\n },\r\n};\r\n\r\nexport interface MyTicketListDto {\r\n id: string;\r\n ticketNumber: string;\r\n title: string;\r\n type: string;\r\n status: string;\r\n priority: string;\r\n commentCount: number;\r\n createdAt: string;\r\n updatedAt?: string;\r\n tenantId?: string;\r\n tenantName?: string;\r\n externalUrl?: string;\r\n isExternallyManaged?: boolean;\r\n}\r\n\r\nexport interface MyTicketDetailDto {\r\n id: string;\r\n ticketNumber: string;\r\n title: string;\r\n description: string;\r\n type: string;\r\n status: string;\r\n priority: string;\r\n resolution?: string;\r\n resolvedAt?: string;\r\n closedAt?: string;\r\n comments: MyCommentDto[];\r\n attachments: MyAttachmentDto[];\r\n createdAt: string;\r\n updatedAt?: string;\r\n tenantId?: string;\r\n tenantName?: string;\r\n solutions?: TicketSolutionDto[];\r\n activeSolution?: TicketSolutionDto;\r\n hasPendingSolution?: boolean;\r\n}\r\n\r\nexport interface MyCommentDto {\r\n id: string;\r\n userName: string;\r\n content: string;\r\n isEdited: boolean;\r\n createdAt: string;\r\n}\r\n\r\nexport interface MyAttachmentDto {\r\n id: string;\r\n fileName: string;\r\n fileSize: number;\r\n createdAt: string;\r\n}\r\n\r\nexport interface MyTicketActivityDto {\r\n id: string;\r\n action: string;\r\n fieldChanged?: string;\r\n oldValue?: string;\r\n newValue?: string;\r\n createdAt: string;\r\n}\r\n\r\nexport interface MyTicketStatsDto {\r\n total: number;\r\n open: number;\r\n inProgress: number;\r\n resolved: number;\r\n closed: number;\r\n}\r\n\r\nexport interface MySatisfactionDto {\r\n id: string;\r\n rating: number;\r\n comment?: string;\r\n submittedAt: string;\r\n}\r\n\r\nexport interface SubmitSatisfactionRequest {\r\n rating: number;\r\n comment?: string;\r\n}\r\n\r\nexport interface CreateMyTicketRequest {\r\n title: string;\r\n description: string;\r\n type: TicketType;\r\n priority?: TicketPriority;\r\n clientContext?: ClientContextDto;\r\n}\r\n\r\nexport interface UpdateMyTicketRequest {\r\n title: string;\r\n description: string;\r\n priority: TicketPriority;\r\n}\r\n\r\nexport interface GetMyTicketsParams {\r\n page?: number;\r\n pageSize?: number;\r\n search?: string;\r\n status?: TicketStatus;\r\n type?: TicketType;\r\n}\r\n\r\nexport const myTicketsApi = {\r\n getAll: (params?: GetMyTicketsParams) =>\r\n api.get<PaginatedResult<MyTicketListDto>>('/api/support/my-tickets', { params }),\r\n\r\n getById: (id: string) =>\r\n api.get<MyTicketDetailDto>(`/api/support/my-tickets/${id}`),\r\n\r\n create: (data: CreateMyTicketRequest) =>\r\n api.post<MyTicketDetailDto>('/api/support/my-tickets', data),\r\n\r\n update: (id: string, data: UpdateMyTicketRequest) =>\r\n api.put<MyTicketDetailDto>(`/api/support/my-tickets/${id}`, data),\r\n\r\n addComment: (id: string, content: string) =>\r\n api.post<MyCommentDto>(`/api/support/my-tickets/${id}/comments`, { content }),\r\n\r\n getStats: () =>\r\n api.get<MyTicketStatsDto>('/api/support/my-tickets/stats'),\r\n\r\n uploadAttachment: (ticketId: string, file: File) => {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n return api.post<MyAttachmentDto>(`/api/support/my-tickets/${ticketId}/attachments`, formData, {\r\n headers: { 'Content-Type': 'multipart/form-data' },\r\n });\r\n },\r\n\r\n downloadAttachment: (ticketId: string, attachmentId: string) =>\r\n api.get<Blob>(`/api/support/my-tickets/${ticketId}/attachments/${attachmentId}`, {\r\n responseType: 'blob',\r\n }),\r\n\r\n deleteAttachment: (ticketId: string, attachmentId: string) =>\r\n api.delete(`/api/support/my-tickets/${ticketId}/attachments/${attachmentId}`),\r\n\r\n getActivities: (ticketId: string, limit: number = 10) =>\r\n api.get<MyTicketActivityDto[]>(`/api/support/my-tickets/${ticketId}/activities`, { params: { limit } }),\r\n\r\n getAttachmentUrl: (ticketId: string, attachmentId: string) =>\r\n `/api/support/my-tickets/${ticketId}/attachments/${attachmentId}`,\r\n\r\n submitSatisfaction: (ticketId: string, data: SubmitSatisfactionRequest) =>\r\n api.post<MySatisfactionDto>(`/api/support/my-tickets/${ticketId}/satisfaction`, data),\r\n\r\n getSatisfaction: (ticketId: string) =>\r\n api.get<MySatisfactionDto | null>(`/api/support/my-tickets/${ticketId}/satisfaction`),\r\n\r\n getSolutions: (ticketId: string) =>\r\n api.get<TicketSolutionDto[]>(`/api/support/my-tickets/${ticketId}/solutions`),\r\n\r\n approveSolution: (ticketId: string) =>\r\n api.patch<TicketSolutionDto>(`/api/support/my-tickets/${ticketId}/solutions/approve`),\r\n\r\n rejectSolution: (ticketId: string, reason: string) =>\r\n api.patch<TicketSolutionDto>(`/api/support/my-tickets/${ticketId}/solutions/reject`, { reason }),\r\n};\r\n","import { api } from './apiClient';\r\n\r\n// Assignment Rule Types\r\nexport interface AssignmentRuleChainDto {\r\n fallbackOrder: number;\r\n fallbackStrategyType: string;\r\n fallbackGroupId: string | null;\r\n}\r\n\r\nexport interface AssignmentRuleDto {\r\n id: string;\r\n tenantId: string | null;\r\n name: string;\r\n priority: number;\r\n strategyType: string;\r\n ticketPriorityFilter: string | null;\r\n ticketTypeFilter: string | null;\r\n targetGroupId: string | null;\r\n maxConcurrentTickets: number;\r\n isActive: boolean;\r\n isFallback: boolean;\r\n fallbackChain: AssignmentRuleChainDto[];\r\n createdAt: string;\r\n updatedAt: string | null;\r\n}\r\n\r\nexport interface CreateAssignmentRuleChainRequest {\r\n fallbackOrder: number;\r\n fallbackStrategyType: string;\r\n fallbackGroupId?: string;\r\n}\r\n\r\nexport interface CreateAssignmentRuleRequest {\r\n name: string;\r\n priority: number;\r\n strategyType: string;\r\n ticketPriorityFilter?: string;\r\n ticketTypeFilter?: string;\r\n targetGroupId?: string;\r\n maxConcurrentTickets?: number;\r\n isFallback?: boolean;\r\n fallbackChain?: CreateAssignmentRuleChainRequest[];\r\n}\r\n\r\nexport interface UpdateAssignmentRuleRequest {\r\n name: string;\r\n priority: number;\r\n strategyType: string;\r\n ticketPriorityFilter?: string;\r\n ticketTypeFilter?: string;\r\n targetGroupId?: string;\r\n maxConcurrentTickets?: number;\r\n isFallback?: boolean;\r\n fallbackChain?: CreateAssignmentRuleChainRequest[];\r\n}\r\n\r\n// Agent Skills Types\r\nexport interface AgentSkillDto {\r\n userId: string;\r\n skillCategory: string;\r\n proficiencyLevel: number;\r\n assignedAt: string;\r\n assignedBy: string | null;\r\n}\r\n\r\nexport interface AgentSkillItemRequest {\r\n skillCategory: string;\r\n proficiencyLevel: number;\r\n}\r\n\r\nexport interface UpdateAgentSkillsRequest {\r\n skills: AgentSkillItemRequest[];\r\n}\r\n\r\n// Agent Availability Types\r\nexport interface AgentAvailabilityDto {\r\n userId: string;\r\n status: string;\r\n maxConcurrentTickets: number;\r\n lastStatusChangeAt: string;\r\n lastHeartbeatAt: string | null;\r\n}\r\n\r\nexport interface UpdateAgentAvailabilityRequest {\r\n status: string;\r\n}\r\n\r\n// Assignment Log Types\r\nexport interface AssignmentLogDto {\r\n id: string;\r\n ticketId: string;\r\n assignedUserId: string | null;\r\n ruleId: string | null;\r\n strategyUsed: string;\r\n reason: string | null;\r\n success: boolean;\r\n createdAt: string;\r\n}\r\n\r\n// Agent Workload Types\r\nexport interface AgentWorkloadDto {\r\n userId: string;\r\n userName: string | null;\r\n activeTicketCount: number;\r\n maxConcurrentTickets: number;\r\n availabilityStatus: string;\r\n skills: AgentSkillDto[];\r\n}\r\n\r\n// Rules API\r\nexport const rulesApi = {\r\n getAll: () =>\r\n api.get<AssignmentRuleDto[]>('/api/support/assignment'),\r\n\r\n getById: (id: string) =>\r\n api.get<AssignmentRuleDto>(`/api/support/assignment/${id}`),\r\n\r\n create: (data: CreateAssignmentRuleRequest) =>\r\n api.post<AssignmentRuleDto>('/api/support/assignment', data),\r\n\r\n update: (id: string, data: UpdateAssignmentRuleRequest) =>\r\n api.put<AssignmentRuleDto>(`/api/support/assignment/${id}`, data),\r\n\r\n delete: (id: string) =>\r\n api.delete(`/api/support/assignment/${id}`),\r\n\r\n activate: (id: string) =>\r\n api.patch(`/api/support/assignment/${id}/activate`),\r\n\r\n deactivate: (id: string) =>\r\n api.patch(`/api/support/assignment/${id}/deactivate`),\r\n};\r\n\r\n// Skills API\r\nexport const skillsApi = {\r\n getAll: () =>\r\n api.get<AgentSkillDto[]>('/api/support/assignment/skills'),\r\n\r\n getByAgent: (userId: string) =>\r\n api.get<AgentSkillDto[]>('/api/support/assignment/skills', {\r\n params: { userId },\r\n }),\r\n\r\n update: (userId: string, data: UpdateAgentSkillsRequest) =>\r\n api.put<AgentSkillDto[]>(`/api/support/assignment/skills/${userId}`, data),\r\n};\r\n\r\n// Availability API\r\nexport const availabilityApi = {\r\n getByAgent: (userId: string) =>\r\n api.get<AgentAvailabilityDto>(`/api/support/assignment/availability/${userId}`),\r\n\r\n update: (userId: string, data: UpdateAgentAvailabilityRequest) =>\r\n api.put<AgentAvailabilityDto>(\r\n `/api/support/assignment/availability/${userId}`,\r\n data\r\n ),\r\n};\r\n\r\n// Logs API\r\nexport const logsApi = {\r\n getByTicket: (ticketId: string, page = 1, pageSize = 50) =>\r\n api.get<AssignmentLogDto[]>('/api/support/assignment/logs', {\r\n params: { ticketId, page, pageSize },\r\n }),\r\n};\r\n\r\n// Workload API\r\nexport const workloadApi = {\r\n getByAgent: (userId: string) =>\r\n api.get<AgentWorkloadDto>(`/api/support/assignment/workload/${userId}`),\r\n\r\n getAll: () =>\r\n api.get<AgentWorkloadDto[]>('/api/support/assignment/workload'),\r\n};\r\n","import { useState, useEffect, useCallback } from 'react';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { supportApi, type TicketingProviderInfoDto } from '@/services/api/ticketApi';\r\n\r\nexport interface UseTicketingProviderReturn {\r\n provider: string;\r\n isGlpi: boolean;\r\n isConfigured: boolean;\r\n glpiBaseUrl?: string;\r\n loading: boolean;\r\n error: string | null;\r\n refresh: () => Promise<void>;\r\n}\r\n\r\nexport function useTicketingProvider(): UseTicketingProviderReturn {\r\n const { currentTenant } = useTenant();\r\n const [data, setData] = useState<TicketingProviderInfoDto | null>(null);\r\n const [loading, setLoading] = useState(!!currentTenant);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const fetchProvider = useCallback(async () => {\r\n if (!currentTenant) {\r\n setData(null);\r\n return;\r\n }\r\n\r\n setLoading(true);\r\n setError(null);\r\n try {\r\n const response = await supportApi.tickets.getTicketingProvider();\r\n setData(response);\r\n } catch {\r\n setError('Failed to load ticketing provider configuration');\r\n setData(null);\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [currentTenant]);\r\n\r\n useEffect(() => {\r\n fetchProvider();\r\n }, [fetchProvider]);\r\n\r\n return {\r\n provider: data?.provider ?? 'SmartStack',\r\n isGlpi: data?.provider === 'Glpi',\r\n isConfigured: data?.isConfigured ?? false,\r\n glpiBaseUrl: data?.glpiBaseUrl ?? undefined,\r\n loading,\r\n error,\r\n refresh: fetchProvider,\r\n };\r\n}\r\n","export const statusColors: Record<string, 'active' | 'inactive' | 'pending' | 'warning' | 'error'> = {\r\n Open: 'pending',\r\n InProgress: 'active',\r\n OnHold: 'warning',\r\n Resolved: 'active',\r\n Closed: 'inactive',\r\n Rejected: 'error',\r\n};\r\n\r\nexport const priorityColors: Record<string, { bg: string; text: string }> = {\r\n Low: { bg: 'var(--info-bg)', text: 'var(--info-text)' },\r\n Medium: { bg: 'var(--warning-bg)', text: 'var(--warning-text)' },\r\n High: { bg: 'var(--error-bg)', text: 'var(--error-text)' },\r\n Critical: { bg: 'var(--error-text)', text: 'white' },\r\n};\r\n","import { useState, useEffect, useCallback, useMemo } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useParams, useNavigate, useSearchParams } from 'react-router-dom';\r\nimport {\r\n ArrowLeft, Loader2, AlertTriangle, X, FileText, Info, MessageSquare,\r\n CheckCircle, Edit2, Save, Search, ArrowUpCircle, Star, ExternalLink, ShieldAlert,\r\n Users, Sparkles\r\n} from 'lucide-react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport DOMPurify from 'dompurify';\r\nimport { TicketConversation } from './TicketConversation';\r\nimport { TicketSidebar } from './TicketSidebar';\r\nimport { TicketWorkflowHorizontal } from './TicketWorkflowHorizontal';\r\nimport { TicketSolutionPanel } from './TicketSolutionPanel';\r\nimport { AiRoutingSuggestionPanel } from './AiRoutingSuggestionPanel';\r\nimport { AiTicketSummary } from './AiTicketSummary';\r\nimport { AiClassificationBadge } from './AiClassificationBadge';\r\nimport { useTicketSignalR, type TicketCommentDto, type TicketUpdateDto } from '@/hooks/useTicketSignalR';\r\nimport {\r\n supportApi,\r\n myTicketsApi,\r\n type TicketDetailDto,\r\n type MyTicketDetailDto,\r\n type TicketStatus,\r\n type TicketPriority,\r\n type TicketActivityDto,\r\n type MyTicketActivityDto,\r\n type TicketActivityAction,\r\n type MySatisfactionDto,\r\n type TicketSolutionDto,\r\n} from '@/services/api/ticketApi';\r\nimport { slaApi } from '@/services/api/supportApi';\r\nimport { logsApi, type AssignmentLogDto } from '@/services/api/assignmentApi';\r\nimport { StatusBadge } from '@/components/ui/DataView';\r\nimport { api } from '@/services/api/apiClient';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { useTicketingProvider } from '@/hooks/useTicketingProvider';\r\nimport { statusColors, priorityColors } from '@/constants/supportTheme';\r\n\r\nexport type TicketViewMode = 'support' | 'client';\r\n\r\ntype TicketTab = 'conversation' | 'description' | 'information' | 'assignment' | 'ai';\r\nconst VALID_TABS: TicketTab[] = ['conversation', 'description', 'information', 'assignment', 'ai'];\r\n\r\ntype TFunction = ReturnType<typeof useTranslation>['t'];\r\n\r\ninterface TicketDetailViewProps {\r\n readonly mode: TicketViewMode;\r\n}\r\n\r\nconst STATUS_DESCRIPTION_KEYS: Record<string, string> = {\r\n Open: 'statusDescriptions.Open',\r\n InProgress: 'statusDescriptions.InProgress',\r\n OnHold: 'statusDescriptions.OnHold',\r\n Resolved: 'statusDescriptions.Resolved',\r\n Closed: 'statusDescriptions.Closed',\r\n Rejected: 'statusDescriptions.Rejected',\r\n};\r\n\r\n// Sub-component: Satisfaction Rating\r\ninterface SatisfactionRatingProps {\r\n readonly satisfaction: MySatisfactionDto | null;\r\n readonly showRatingForm: boolean;\r\n readonly ratingValue: number;\r\n readonly ratingComment: string;\r\n readonly submittingRating: boolean;\r\n readonly onShowForm: () => void;\r\n readonly onRatingChange: (value: number) => void;\r\n readonly onCommentChange: (value: string) => void;\r\n readonly onCancel: () => void;\r\n readonly onSubmit: () => Promise<void>;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction SatisfactionRating({\r\n satisfaction,\r\n showRatingForm,\r\n ratingValue,\r\n ratingComment,\r\n submittingRating,\r\n onShowForm,\r\n onRatingChange,\r\n onCommentChange,\r\n onCancel,\r\n onSubmit,\r\n t,\r\n}: Readonly<SatisfactionRatingProps>) {\r\n if (satisfaction) {\r\n return (\r\n <div>\r\n <h3 className=\"font-medium flex items-center gap-2 mb-2\">\r\n <Star className=\"w-4 h-4 text-amber-500 fill-amber-500\" />\r\n {t('satisfaction.yourRating', 'Votre évaluation')}\r\n </h3>\r\n <div className=\"flex items-center gap-1 mb-2\">\r\n {[1, 2, 3, 4, 5].map((star) => (\r\n <Star\r\n key={star}\r\n className={`w-5 h-5 ${\r\n star <= satisfaction.rating\r\n ? 'text-amber-500 fill-amber-500'\r\n : 'text-[var(--text-tertiary)]'\r\n }`}\r\n />\r\n ))}\r\n <span className=\"ml-2 text-sm text-[var(--text-secondary)]\">\r\n {satisfaction.rating}/5\r\n </span>\r\n </div>\r\n {satisfaction.comment && (\r\n <p className=\"text-sm text-[var(--text-secondary)] italic\">\r\n \"{satisfaction.comment}\"\r\n </p>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n if (showRatingForm) {\r\n return (\r\n <div>\r\n <h3 className=\"font-medium flex items-center gap-2 mb-3\">\r\n <Star className=\"w-4 h-4 text-amber-500\" />\r\n {t('satisfaction.rateTitle', 'Évaluez cette résolution')}\r\n </h3>\r\n <div className=\"flex items-center gap-1 mb-3\">\r\n {[1, 2, 3, 4, 5].map((star) => (\r\n <button\r\n key={star}\r\n type=\"button\"\r\n onClick={() => onRatingChange(star)}\r\n className=\"p-1 hover:scale-110 transition-transform\"\r\n >\r\n <Star\r\n className={`w-7 h-7 transition-colors ${\r\n star <= ratingValue\r\n ? 'text-amber-500 fill-amber-500'\r\n : 'text-[var(--text-tertiary)] hover:text-amber-300'\r\n }`}\r\n />\r\n </button>\r\n ))}\r\n </div>\r\n <textarea\r\n value={ratingComment}\r\n onChange={(e) => onCommentChange(e.target.value)}\r\n placeholder={t('satisfaction.commentPlaceholder', 'Commentaire (optionnel)...')}\r\n className=\"input w-full h-20 mb-3 text-sm\"\r\n />\r\n <div className=\"flex gap-2\">\r\n <button onClick={onCancel} className=\"btn btn-secondary btn-sm\">\r\n {t('common.cancel', 'Annuler')}\r\n </button>\r\n <button\r\n onClick={onSubmit}\r\n disabled={ratingValue === 0 || submittingRating}\r\n className=\"btn btn-primary btn-sm\"\r\n >\r\n {submittingRating ? (\r\n <Loader2 className=\"w-4 h-4 animate-spin mr-1\" />\r\n ) : (\r\n <Star className=\"w-4 h-4 mr-1\" />\r\n )}\r\n {t('satisfaction.submit', 'Envoyer')}\r\n </button>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"flex items-center justify-between\">\r\n <div>\r\n <h3 className=\"font-medium flex items-center gap-2\">\r\n <Star className=\"w-4 h-4 text-amber-500\" />\r\n {t('satisfaction.askTitle', 'Comment évaluez-vous cette résolution ?')}\r\n </h3>\r\n <p className=\"text-sm text-[var(--text-secondary)] mt-1\">\r\n {t('satisfaction.askDescription', 'Votre avis nous aide à améliorer notre service.')}\r\n </p>\r\n </div>\r\n <button onClick={onShowForm} className=\"btn btn-primary btn-sm\">\r\n <Star className=\"w-4 h-4 mr-1\" />\r\n {t('satisfaction.rateButton', 'Évaluer')}\r\n </button>\r\n </div>\r\n );\r\n}\r\n\r\n// Sub-component: Resolve Modal\r\ninterface ResolveModalProps {\r\n readonly show: boolean;\r\n readonly resolution: string;\r\n readonly saving: boolean;\r\n readonly onResolutionChange: (value: string) => void;\r\n readonly onClose: () => void;\r\n readonly onResolve: () => Promise<void>;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction ResolveModal({ show, resolution, saving, onResolutionChange, onClose, onResolve, t }: Readonly<ResolveModalProps>) {\r\n if (!show) return null;\r\n\r\n return (\r\n <div className=\"fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50\">\r\n <div className=\"card w-full max-w-lg p-6 m-4\">\r\n <h2 className=\"text-xl font-bold mb-4\">{t('resolve.title', 'Proposer une solution')}</h2>\r\n <textarea\r\n value={resolution}\r\n onChange={(e) => onResolutionChange(e.target.value)}\r\n placeholder={t('resolve.placeholder', 'Décrivez la résolution...')}\r\n className=\"input w-full h-32 mb-4\"\r\n />\r\n <div className=\"flex justify-end gap-2\">\r\n <button onClick={onClose} className=\"btn btn-secondary\">\r\n {t('common.cancel', 'Annuler')}\r\n </button>\r\n <button\r\n onClick={onResolve}\r\n disabled={!resolution.trim() || saving}\r\n className=\"btn btn-primary\"\r\n >\r\n {saving && <Loader2 className=\"w-4 h-4 animate-spin mr-2\" />}\r\n {t('resolve.confirm', 'Proposer')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// Sub-component: Assign Modal\r\ninterface AssignModalProps {\r\n readonly show: boolean;\r\n readonly filteredUsers: Array<{ id: string; email: string; fullName: string }>;\r\n readonly selectedUserIds: string[];\r\n readonly selectedGroupIds: string[];\r\n readonly groups: Array<{ id: string; name: string; memberCount: number }>;\r\n readonly userSearchQuery: string;\r\n readonly groupSearchQuery: string;\r\n readonly loadingUsers: boolean;\r\n readonly loadingGroups: boolean;\r\n readonly saving: boolean;\r\n readonly activeTab: 'users' | 'groups';\r\n readonly onUserSearchChange: (value: string) => void;\r\n readonly onGroupSearchChange: (value: string) => void;\r\n readonly onSelectUser: (id: string) => void;\r\n readonly onSelectGroup: (id: string) => void;\r\n readonly onTabChange: (tab: 'users' | 'groups') => void;\r\n readonly onClose: () => void;\r\n readonly onAssign: () => Promise<void>;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction AssignModal({\r\n show,\r\n filteredUsers,\r\n selectedUserIds,\r\n selectedGroupIds,\r\n groups,\r\n userSearchQuery,\r\n groupSearchQuery,\r\n loadingUsers,\r\n loadingGroups,\r\n saving,\r\n activeTab,\r\n onUserSearchChange,\r\n onGroupSearchChange,\r\n onSelectUser,\r\n onSelectGroup,\r\n onTabChange,\r\n onClose,\r\n onAssign,\r\n t,\r\n}: Readonly<AssignModalProps>) {\r\n const filteredGroups = useMemo(() => {\r\n if (!groupSearchQuery.trim()) return groups;\r\n const query = groupSearchQuery.toLowerCase();\r\n return groups.filter(g => g.name.toLowerCase().includes(query));\r\n }, [groups, groupSearchQuery]);\r\n\r\n if (!show) return null;\r\n\r\n return (\r\n <div className=\"fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50\">\r\n <div className=\"card w-full max-w-lg p-6 m-4 max-h-[80vh] flex flex-col\">\r\n <h2 className=\"text-xl font-bold mb-4\">{t('assign.title', 'Assigner le ticket')}</h2>\r\n\r\n {/* Tab buttons */}\r\n <div className=\"flex gap-2 mb-4 border-b border-[var(--border-color)]\">\r\n <button\r\n onClick={() => onTabChange('users')}\r\n className={`pb-2 px-3 font-medium transition-colors border-b-2 ${\r\n activeTab === 'users'\r\n ? 'border-[var(--color-accent-500)] text-[var(--color-accent-500)]'\r\n : 'border-transparent text-[var(--text-secondary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n >\r\n {t('assign.tabUsers', 'Utilisateurs')}\r\n </button>\r\n <button\r\n onClick={() => onTabChange('groups')}\r\n className={`pb-2 px-3 font-medium transition-colors border-b-2 ${\r\n activeTab === 'groups'\r\n ? 'border-[var(--color-accent-500)] text-[var(--color-accent-500)]'\r\n : 'border-transparent text-[var(--text-secondary)] hover:text-[var(--text-primary)]'\r\n }`}\r\n >\r\n {t('assign.tabGroups', 'Groupes')}\r\n </button>\r\n </div>\r\n\r\n {/* Search input */}\r\n <div className=\"relative mb-3\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-tertiary)]\" />\r\n <input\r\n type=\"text\"\r\n value={activeTab === 'users' ? userSearchQuery : groupSearchQuery}\r\n onChange={(e) => activeTab === 'users' ? onUserSearchChange(e.target.value) : onGroupSearchChange(e.target.value)}\r\n placeholder={activeTab === 'users' ? t('assign.searchUserPlaceholder', 'Rechercher un utilisateur...') : t('assign.searchGroupPlaceholder', 'Rechercher un groupe...')}\r\n className=\"input w-full pl-10\"\r\n autoFocus\r\n />\r\n </div>\r\n\r\n {/* Content area */}\r\n <div className=\"flex-1 overflow-y-auto mb-4\">\r\n {activeTab === 'users' && (\r\n <>\r\n {loadingUsers ? (\r\n <div className=\"flex items-center justify-center py-8\">\r\n <Loader2 className=\"w-6 h-6 animate-spin text-[var(--color-primary-600)]\" />\r\n </div>\r\n ) : (\r\n <div className=\"border border-[var(--border-color)] rounded-lg overflow-hidden\">\r\n {filteredUsers.length === 0 ? (\r\n <div className=\"px-4 py-6 text-center text-sm text-[var(--text-tertiary)]\">\r\n {t('assign.noUsersFound', 'Aucun utilisateur trouvé')}\r\n </div>\r\n ) : (\r\n filteredUsers.map((user) => (\r\n <button\r\n key={user.id}\r\n onClick={() => onSelectUser(user.id)}\r\n className={`w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-[var(--bg-secondary)] transition-colors border-t border-[var(--border-color)] first:border-t-0 ${\r\n selectedUserIds.includes(user.id) ? 'bg-[var(--color-accent-500)]/10' : ''\r\n }`}\r\n >\r\n <div className=\"w-5 h-5 border-2 border-[var(--border-color)] rounded flex items-center justify-center flex-shrink-0\">\r\n {selectedUserIds.includes(user.id) && (\r\n <div className=\"w-3 h-3 bg-[var(--color-accent-500)] rounded-sm\" />\r\n )}\r\n </div>\r\n <div className=\"w-8 h-8 rounded-full bg-[var(--color-accent-500)] flex items-center justify-center text-white font-medium text-sm flex-shrink-0\">\r\n {user.fullName.charAt(0).toUpperCase()}\r\n </div>\r\n <div className=\"flex-1 min-w-0\">\r\n <p className=\"font-medium truncate\">{user.fullName}</p>\r\n <p className=\"text-xs text-[var(--text-tertiary)] truncate\">{user.email}</p>\r\n </div>\r\n </button>\r\n ))\r\n )}\r\n </div>\r\n )}\r\n </>\r\n )}\r\n\r\n {activeTab === 'groups' && (\r\n <>\r\n {loadingGroups ? (\r\n <div className=\"flex items-center justify-center py-8\">\r\n <Loader2 className=\"w-6 h-6 animate-spin text-[var(--color-primary-600)]\" />\r\n </div>\r\n ) : (\r\n <div className=\"border border-[var(--border-color)] rounded-lg overflow-hidden\">\r\n {filteredGroups.length === 0 ? (\r\n <div className=\"px-4 py-6 text-center text-sm text-[var(--text-tertiary)]\">\r\n {t('assign.noGroupsFound', 'Aucun groupe trouvé')}\r\n </div>\r\n ) : (\r\n filteredGroups.map((group) => (\r\n <button\r\n key={group.id}\r\n onClick={() => onSelectGroup(group.id)}\r\n className={`w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-[var(--bg-secondary)] transition-colors border-t border-[var(--border-color)] first:border-t-0 ${\r\n selectedGroupIds.includes(group.id) ? 'bg-[var(--color-accent-500)]/10' : ''\r\n }`}\r\n >\r\n <div className=\"w-5 h-5 border-2 border-[var(--border-color)] rounded flex items-center justify-center flex-shrink-0\">\r\n {selectedGroupIds.includes(group.id) && (\r\n <div className=\"w-3 h-3 bg-[var(--color-accent-500)] rounded-sm\" />\r\n )}\r\n </div>\r\n <div className=\"w-8 h-8 rounded-full bg-[var(--color-accent-500)] flex items-center justify-center text-white font-medium text-sm flex-shrink-0\">\r\n {group.name.charAt(0).toUpperCase()}\r\n </div>\r\n <div className=\"flex-1 min-w-0\">\r\n <p className=\"font-medium truncate\">{group.name}</p>\r\n </div>\r\n <span className=\"text-xs px-2 py-1 rounded bg-[var(--bg-tertiary)] text-[var(--text-secondary)] flex-shrink-0\">\r\n {group.memberCount}\r\n </span>\r\n </button>\r\n ))\r\n )}\r\n </div>\r\n )}\r\n </>\r\n )}\r\n </div>\r\n\r\n {/* Summary bar */}\r\n <div className=\"text-sm text-[var(--text-secondary)] mb-4 px-3 py-2 bg-[var(--bg-secondary)] rounded\">\r\n {t('assign.selectedSummary', '{{userCount}} utilisateur(s), {{groupCount}} groupe(s) sélectionné(s)', {\r\n userCount: selectedUserIds.length,\r\n groupCount: selectedGroupIds.length,\r\n })}\r\n </div>\r\n\r\n {/* Buttons */}\r\n <div className=\"flex justify-end gap-2\">\r\n <button\r\n onClick={() => {\r\n onClose();\r\n onUserSearchChange('');\r\n onGroupSearchChange('');\r\n }}\r\n className=\"btn btn-secondary\"\r\n >\r\n {t('common.cancel', 'Annuler')}\r\n </button>\r\n <button onClick={onAssign} disabled={saving || loadingUsers || loadingGroups} className=\"btn btn-primary\">\r\n {saving && <Loader2 className=\"w-4 h-4 animate-spin mr-2\" />}\r\n {t('assign.confirm', 'Assigner')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// Sub-component: Escalate Modal\r\nconst ESCALATION_LEVEL_VALUES = ['Supervisor', 'Manager', 'Director'] as const;\r\n\r\ninterface EscalateModalProps {\r\n readonly show: boolean;\r\n readonly escalationLevel: string;\r\n readonly escalationReason: string;\r\n readonly saving: boolean;\r\n readonly onLevelChange: (value: string) => void;\r\n readonly onReasonChange: (value: string) => void;\r\n readonly onClose: () => void;\r\n readonly onEscalate: () => Promise<void>;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction EscalateModal({\r\n show,\r\n escalationLevel,\r\n escalationReason,\r\n saving,\r\n onLevelChange,\r\n onReasonChange,\r\n onClose,\r\n onEscalate,\r\n t,\r\n}: Readonly<EscalateModalProps>) {\r\n if (!show) return null;\r\n\r\n return (\r\n <div className=\"fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50\">\r\n <div className=\"card w-full max-w-lg p-6 m-4\">\r\n <div className=\"flex items-center gap-3 mb-4\">\r\n <div className=\"w-10 h-10 rounded-full bg-[var(--warning-bg)] flex items-center justify-center\">\r\n <ArrowUpCircle className=\"w-5 h-5 text-[var(--warning-text)]\" />\r\n </div>\r\n <h2 className=\"text-xl font-bold\">{t('escalate.title')}</h2>\r\n </div>\r\n\r\n <p className=\"text-sm text-[var(--text-secondary)] mb-4\">\r\n {t('escalate.description')}\r\n </p>\r\n\r\n <div className=\"mb-4\">\r\n <label className=\"block text-sm font-medium mb-2\">{t('escalate.level')}</label>\r\n <div className=\"space-y-2\">\r\n {ESCALATION_LEVEL_VALUES.map((value) => (\r\n <button\r\n key={value}\r\n onClick={() => onLevelChange(value)}\r\n className={`w-full flex items-center gap-3 px-4 py-3 text-left rounded-lg border transition-colors ${\r\n escalationLevel === value\r\n ? 'border-[var(--warning-border)] bg-[var(--warning-bg)]'\r\n : 'border-[var(--border-color)] hover:bg-[var(--bg-secondary)]'\r\n }`}\r\n >\r\n <div\r\n className={`w-4 h-4 rounded-full border-2 flex items-center justify-center ${\r\n escalationLevel === value\r\n ? 'border-[var(--warning-text)]'\r\n : 'border-[var(--border-color)]'\r\n }`}\r\n >\r\n {escalationLevel === value && (\r\n <div className=\"w-2 h-2 rounded-full bg-[var(--warning-text)]\" />\r\n )}\r\n </div>\r\n <div className=\"flex-1\">\r\n <p className=\"font-medium\">{t(`escalate.levels.${value}`)}</p>\r\n <p className=\"text-xs text-[var(--text-tertiary)]\">{t(`escalate.levels.${value}Desc`)}</p>\r\n </div>\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n <div className=\"mb-4\">\r\n <label className=\"block text-sm font-medium mb-2\">{t('escalate.reason')}</label>\r\n <textarea\r\n value={escalationReason}\r\n onChange={(e) => onReasonChange(e.target.value)}\r\n placeholder={t('escalate.reasonPlaceholder')}\r\n className=\"input w-full h-24\"\r\n />\r\n </div>\r\n\r\n <div className=\"flex justify-end gap-2\">\r\n <button\r\n onClick={() => {\r\n onClose();\r\n onLevelChange('Supervisor');\r\n onReasonChange('');\r\n }}\r\n className=\"btn btn-secondary\"\r\n >\r\n {t('common.cancel')}\r\n </button>\r\n <button\r\n onClick={onEscalate}\r\n disabled={saving}\r\n className=\"btn border border-[var(--warning-border)] bg-[var(--warning-bg)] text-[var(--warning-text)] hover:bg-[var(--warning-border)]\"\r\n >\r\n {saving && <Loader2 className=\"w-4 h-4 animate-spin mr-2\" />}\r\n <ArrowUpCircle className=\"w-4 h-4 mr-2\" />\r\n {t('escalate.confirm')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// Unified types\r\ninterface UnifiedAttachment {\r\n id: string;\r\n fileName: string;\r\n contentType?: string;\r\n fileSize: number;\r\n createdAt: string;\r\n}\r\n\r\ninterface UnifiedComment {\r\n id: string;\r\n userName: string;\r\n content: string;\r\n isInternal?: boolean;\r\n isEdited: boolean;\r\n createdAt: string;\r\n}\r\n\r\ninterface UnifiedTicket {\r\n id: string;\r\n ticketNumber: string;\r\n title: string;\r\n description: string;\r\n type: string;\r\n status: string;\r\n priority: string;\r\n createdByName?: string;\r\n assignedToName?: string;\r\n resolution?: string;\r\n resolvedAt?: string;\r\n closedAt?: string;\r\n comments: UnifiedComment[];\r\n attachments: UnifiedAttachment[];\r\n createdAt: string;\r\n updatedAt?: string;\r\n clientContext?: TicketDetailDto['clientContext'];\r\n assignees?: Array<{ userId: string; email: string; fullName: string; isPrimary: boolean; assignedViaGroupName?: string }>;\r\n externalProvider?: string;\r\n externalId?: string;\r\n externalUrl?: string;\r\n isExternallyManaged?: boolean;\r\n solutions?: TicketSolutionDto[];\r\n activeSolution?: TicketSolutionDto;\r\n hasPendingSolution?: boolean;\r\n}\r\n\r\nfunction normalizeSupportTicket(supportTicket: TicketDetailDto): UnifiedTicket {\r\n return {\r\n id: supportTicket.id,\r\n ticketNumber: supportTicket.ticketNumber,\r\n title: supportTicket.title,\r\n description: supportTicket.description,\r\n type: supportTicket.type,\r\n status: supportTicket.status,\r\n priority: supportTicket.priority,\r\n createdByName: supportTicket.createdBy?.fullName,\r\n assignedToName: supportTicket.assignedTo?.fullName,\r\n resolution: supportTicket.resolution,\r\n resolvedAt: supportTicket.resolvedAt,\r\n closedAt: supportTicket.closedAt,\r\n assignees: supportTicket.assignees,\r\n comments: supportTicket.comments.map(c => ({\r\n id: c.id,\r\n userName: c.userName,\r\n content: c.content,\r\n isInternal: c.isInternal,\r\n isEdited: c.isEdited,\r\n createdAt: c.createdAt,\r\n })),\r\n attachments: supportTicket.attachments.map(a => ({\r\n id: a.id,\r\n fileName: a.fileName,\r\n contentType: a.contentType,\r\n fileSize: a.fileSize,\r\n createdAt: a.createdAt,\r\n })),\r\n createdAt: supportTicket.createdAt,\r\n updatedAt: supportTicket.updatedAt,\r\n clientContext: supportTicket.clientContext,\r\n externalProvider: supportTicket.externalProvider,\r\n externalId: supportTicket.externalId,\r\n externalUrl: supportTicket.externalUrl,\r\n isExternallyManaged: supportTicket.isExternallyManaged,\r\n solutions: supportTicket.solutions,\r\n activeSolution: supportTicket.activeSolution,\r\n hasPendingSolution: supportTicket.hasPendingSolution,\r\n };\r\n}\r\n\r\nfunction normalizeClientTicket(clientTicket: MyTicketDetailDto): UnifiedTicket {\r\n return {\r\n id: clientTicket.id,\r\n ticketNumber: clientTicket.ticketNumber,\r\n title: clientTicket.title,\r\n description: clientTicket.description,\r\n type: clientTicket.type,\r\n status: clientTicket.status,\r\n priority: clientTicket.priority,\r\n resolution: clientTicket.resolution,\r\n resolvedAt: clientTicket.resolvedAt,\r\n closedAt: clientTicket.closedAt,\r\n comments: clientTicket.comments.map(c => ({\r\n id: c.id,\r\n userName: c.userName,\r\n content: c.content,\r\n isInternal: false,\r\n isEdited: c.isEdited,\r\n createdAt: c.createdAt,\r\n })),\r\n attachments: clientTicket.attachments.map(a => ({\r\n id: a.id,\r\n fileName: a.fileName,\r\n fileSize: a.fileSize,\r\n createdAt: a.createdAt,\r\n })),\r\n createdAt: clientTicket.createdAt,\r\n updatedAt: clientTicket.updatedAt,\r\n solutions: clientTicket.solutions,\r\n activeSolution: clientTicket.activeSolution,\r\n hasPendingSolution: clientTicket.hasPendingSolution,\r\n };\r\n}\r\n\r\nfunction normalizeTicket(ticket: TicketDetailDto | MyTicketDetailDto, mode: TicketViewMode): UnifiedTicket {\r\n return mode === 'support'\r\n ? normalizeSupportTicket(ticket as TicketDetailDto)\r\n : normalizeClientTicket(ticket as MyTicketDetailDto);\r\n}\r\n\r\nconst useTicketDetailState = () => {\r\n const [ticket, setTicket] = useState<UnifiedTicket | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n const [saving, setSaving] = useState(false);\r\n const [uploading, setUploading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const [activities, setActivities] = useState<(TicketActivityDto | MyTicketActivityDto)[]>([]);\r\n const [isEditing, setIsEditing] = useState(false);\r\n const [editForm, setEditForm] = useState({ title: '', priority: 'Medium' as TicketPriority });\r\n const [showAssignModal, setShowAssignModal] = useState(false);\r\n const [showResolveModal, setShowResolveModal] = useState(false);\r\n const [showEscalateModal, setShowEscalateModal] = useState(false);\r\n const [resolution, setResolution] = useState('');\r\n const [escalationLevel, setEscalationLevel] = useState<string>('Supervisor');\r\n const [escalationReason, setEscalationReason] = useState('');\r\n const [users, setUsers] = useState<Array<{ id: string; email: string; fullName: string }>>([]);\r\n const [selectedUserIds, setSelectedUserIds] = useState<string[]>([]);\r\n const [selectedGroupIds, setSelectedGroupIds] = useState<string[]>([]);\r\n const [loadingUsers, setLoadingUsers] = useState(false);\r\n const [userSearchQuery, setUserSearchQuery] = useState('');\r\n const [groupSearchQuery, setGroupSearchQuery] = useState('');\r\n const [groups, setGroups] = useState<Array<{ id: string; name: string; memberCount: number }>>([]);\r\n const [loadingGroups, setLoadingGroups] = useState(false);\r\n const [assignModalTab, setAssignModalTab] = useState<'users' | 'groups'>('users');\r\n const [satisfaction, setSatisfaction] = useState<MySatisfactionDto | null>(null);\r\n const [showRatingForm, setShowRatingForm] = useState(false);\r\n const [ratingValue, setRatingValue] = useState(0);\r\n const [ratingComment, setRatingComment] = useState('');\r\n const [submittingRating, setSubmittingRating] = useState(false);\r\n\r\n return {\r\n ticket, setTicket, loading, setLoading, saving, setSaving, uploading, setUploading,\r\n error, setError, activities, setActivities,\r\n isEditing, setIsEditing, editForm, setEditForm, showAssignModal, setShowAssignModal,\r\n showResolveModal, setShowResolveModal, showEscalateModal, setShowEscalateModal,\r\n resolution, setResolution, escalationLevel, setEscalationLevel,\r\n escalationReason, setEscalationReason, users, setUsers, selectedUserIds, setSelectedUserIds,\r\n selectedGroupIds, setSelectedGroupIds, loadingUsers, setLoadingUsers, userSearchQuery, setUserSearchQuery,\r\n groupSearchQuery, setGroupSearchQuery, groups, setGroups, loadingGroups, setLoadingGroups,\r\n assignModalTab, setAssignModalTab,\r\n satisfaction, setSatisfaction, showRatingForm, setShowRatingForm,\r\n ratingValue, setRatingValue, ratingComment, setRatingComment, submittingRating, setSubmittingRating\r\n };\r\n};\r\n\r\nconst getCurrentUserName = (isSupport: boolean, user: any): string => {\r\n if (isSupport) return user?.email || '';\r\n return (user?.firstName && user?.lastName ? `${user.firstName} ${user.lastName}` : user?.email) || '';\r\n};\r\n\r\nconst updateTabInUrl = (activeTab: TicketTab, searchParams: URLSearchParams, setSearchParams: any) => {\r\n const currentTab = searchParams.get('tab');\r\n if (activeTab === 'conversation') {\r\n if (currentTab) {\r\n searchParams.delete('tab');\r\n setSearchParams(searchParams, { replace: true });\r\n }\r\n } else if (currentTab !== activeTab) {\r\n setSearchParams({ tab: activeTab }, { replace: true });\r\n }\r\n};\r\n\r\nconst getInitialTabFromUrl = (searchParams: URLSearchParams, validTabs: TicketTab[]): TicketTab => {\r\n const tabParam = searchParams.get('tab');\r\n if (tabParam && validTabs.includes(tabParam as TicketTab)) {\r\n return tabParam as TicketTab;\r\n }\r\n return 'conversation';\r\n};\r\n\r\n// Sub-component: Ticket Header\r\ninterface TicketHeaderProps {\r\n readonly ticket: any;\r\n readonly isSupport: boolean;\r\n readonly isEditing: boolean;\r\n readonly saving: boolean;\r\n readonly editForm: { title: string; priority: string };\r\n readonly error: string | null;\r\n readonly onEditToggle: (edit: boolean) => void;\r\n readonly onSave: () => Promise<void>;\r\n readonly onEditFormChange: (form: any) => void;\r\n readonly onErrorClear: () => void;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction TicketHeader({\r\n ticket, isSupport, isEditing, saving, editForm, error,\r\n onEditToggle, onSave, onEditFormChange, onErrorClear, t\r\n}: TicketHeaderProps) {\r\n return (\r\n <div className=\"flex-shrink-0 border-b border-[var(--border-color)] bg-[var(--bg-card)] px-4 py-3\">\r\n <div className=\"flex items-center gap-3\">\r\n <button onClick={() => window.history.back()} className=\"btn btn-ghost p-2\">\r\n <ArrowLeft className=\"w-5 h-5\" />\r\n </button>\r\n\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"flex items-center gap-2 mb-1\">\r\n <span className=\"font-mono text-sm text-[var(--text-secondary)]\">{ticket.ticketNumber}</span>\r\n {ticket.externalId && ticket.externalUrl && (\r\n <a\r\n href={ticket.externalUrl}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-[var(--info-bg)] text-[var(--info-text)] border border-[var(--info-border)] hover:opacity-80\"\r\n >\r\n <ExternalLink className=\"w-3 h-3\" />\r\n GLPI #{ticket.externalId}\r\n </a>\r\n )}\r\n <StatusBadge status={statusColors[ticket.status] || 'pending'} label={ticket.status} />\r\n <span\r\n className=\"px-2 py-0.5 rounded text-xs font-medium\"\r\n style={{\r\n backgroundColor: priorityColors[ticket.priority]?.bg,\r\n color: priorityColors[ticket.priority]?.text,\r\n }}\r\n >\r\n {ticket.priority}\r\n </span>\r\n </div>\r\n {isSupport && isEditing ? (\r\n <input\r\n type=\"text\"\r\n value={editForm.title}\r\n onChange={(e) => onEditFormChange({ ...editForm, title: e.target.value })}\r\n className=\"input w-full text-lg font-bold\"\r\n />\r\n ) : (\r\n <h1 className=\"text-lg font-bold truncate\">{ticket.title}</h1>\r\n )}\r\n </div>\r\n\r\n <div className=\"flex items-center gap-2\">\r\n {isSupport && isEditing ? (\r\n <>\r\n <button onClick={() => onEditToggle(false)} className=\"btn btn-ghost p-2\">\r\n <X className=\"w-5 h-5\" />\r\n </button>\r\n <button onClick={onSave} disabled={saving} className=\"btn btn-primary\">\r\n {saving ? <Loader2 className=\"w-4 h-4 animate-spin\" /> : <Save className=\"w-4 h-4\" />}\r\n </button>\r\n </>\r\n ) : (\r\n <>\r\n {isSupport && !ticket.isExternallyManaged && (\r\n <button onClick={() => onEditToggle(true)} className=\"btn btn-ghost p-2\" title=\"Modifier\">\r\n <Edit2 className=\"w-5 h-5\" />\r\n </button>\r\n )}\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {error && (\r\n <div className=\"mt-3 p-3 rounded-lg bg-[var(--error-bg)] text-[var(--error-text)] flex items-center gap-2\">\r\n <AlertTriangle className=\"w-4 h-4 flex-shrink-0\" />\r\n <span className=\"text-sm flex-1\">{error}</span>\r\n <button onClick={onErrorClear} className=\"p-1 hover:bg-[var(--error-border)] rounded\">\r\n <X className=\"w-4 h-4\" />\r\n </button>\r\n </div>\r\n )}\r\n\r\n {ticket.isExternallyManaged && (\r\n <div className=\"mt-3 p-3 rounded-lg bg-[var(--info-bg)] border border-[var(--info-border)] flex items-center gap-3\">\r\n <ShieldAlert className=\"w-5 h-5 text-[var(--info-text)] flex-shrink-0\" />\r\n <div className=\"flex-1\">\r\n <p className=\"text-sm font-medium text-[var(--info-text)]\">\r\n {t('glpi.readOnlyBanner', 'This ticket is managed by GLPI and is read-only.')}\r\n </p>\r\n </div>\r\n {ticket.externalUrl && (\r\n <a\r\n href={ticket.externalUrl}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"btn btn-sm bg-[var(--info-bg)] text-[var(--info-text)] border border-[var(--info-border)] hover:opacity-80 flex items-center gap-1.5\"\r\n >\r\n <ExternalLink className=\"w-4 h-4\" />\r\n {t('glpi.viewInGlpi', 'View in GLPI')}\r\n </a>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\n// Sub-component: Ticket Info Panels\r\ninterface TicketInfoPanelsProps {\r\n readonly ticket: any;\r\n readonly isSupport: boolean;\r\n readonly satisfaction: any;\r\n readonly showRatingForm: boolean;\r\n readonly ratingValue: number;\r\n readonly ratingComment: string;\r\n readonly submittingRating: boolean;\r\n readonly onShowRatingForm: () => void;\r\n readonly onRatingChange: (value: number) => void;\r\n readonly onCommentChange: (value: string) => void;\r\n readonly onCancelRating: () => void;\r\n readonly onSubmitRating: () => Promise<void>;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction TicketInfoPanels({\r\n ticket, isSupport, satisfaction, showRatingForm, ratingValue, ratingComment, submittingRating,\r\n onShowRatingForm, onRatingChange, onCommentChange, onCancelRating, onSubmitRating, t\r\n}: TicketInfoPanelsProps) {\r\n return (\r\n <>\r\n {!isSupport && (\r\n <div className=\"mt-3 p-3 rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)]\">\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {t(STATUS_DESCRIPTION_KEYS[ticket.status] || 'statusDescriptions.InProgress')}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {ticket.resolution && (\r\n <div className=\"mt-3 p-4 rounded-lg bg-[var(--success-bg)] border border-[var(--success-border)]\">\r\n <h3 className=\"font-medium flex items-center gap-2 text-[var(--success-text)] mb-1\">\r\n <CheckCircle className=\"w-4 h-4\" />\r\n {t('resolution.title', 'Résolution')}\r\n </h3>\r\n <p className=\"text-sm whitespace-pre-wrap\">{ticket.resolution}</p>\r\n </div>\r\n )}\r\n\r\n {!isSupport && (ticket.status === 'Resolved' || ticket.status === 'Closed') && (\r\n <div className=\"mt-3 p-4 rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)]\">\r\n <SatisfactionRating\r\n satisfaction={satisfaction}\r\n showRatingForm={showRatingForm}\r\n ratingValue={ratingValue}\r\n ratingComment={ratingComment}\r\n submittingRating={submittingRating}\r\n onShowForm={onShowRatingForm}\r\n onRatingChange={onRatingChange}\r\n onCommentChange={onCommentChange}\r\n onCancel={onCancelRating}\r\n onSubmit={onSubmitRating}\r\n t={t}\r\n />\r\n </div>\r\n )}\r\n </>\r\n );\r\n}\r\n\r\n// Sub-component: Tab Buttons\r\ninterface TabButtonsProps {\r\n readonly activeTab: TicketTab;\r\n readonly commentCount: number;\r\n readonly isSupport: boolean;\r\n readonly assigneeCount: number;\r\n readonly onTabChange: (tab: TicketTab) => void;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction TabButtons({ activeTab, commentCount, isSupport, assigneeCount, onTabChange, t }: TabButtonsProps) {\r\n const tabConfig = [\r\n { id: 'conversation' as const, icon: MessageSquare, label: t('tabs.conversation'), badge: commentCount },\r\n { id: 'description' as const, icon: FileText, label: t('tabs.description'), badge: 0 },\r\n { id: 'information' as const, icon: Info, label: t('tabs.information'), badge: 0 },\r\n ...(isSupport ? [\r\n { id: 'assignment' as const, icon: Users, label: t('tabs.assignment'), badge: assigneeCount },\r\n { id: 'ai' as const, icon: Sparkles, label: t('tabs.ai'), badge: 0 },\r\n ] : []),\r\n ];\r\n\r\n return (\r\n <div className=\"flex-shrink-0 flex border-b border-[var(--border-color)] bg-[var(--bg-card)]\">\r\n {tabConfig.map(({ id, icon: Icon, label, badge }) => (\r\n <button\r\n key={id}\r\n onClick={() => onTabChange(id)}\r\n className={`flex items-center gap-2 px-4 py-2.5 text-sm font-medium transition-colors border-b-2 ${\r\n activeTab === id\r\n ? 'border-[var(--color-accent-600)] text-[var(--color-accent-600)]'\r\n : 'border-transparent text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:border-[var(--border-color)]'\r\n }`}\r\n >\r\n <Icon className=\"w-4 h-4\" />\r\n {label}\r\n {badge > 0 && (\r\n <span className=\"text-xs px-1.5 py-0.5 rounded-full bg-[var(--bg-tertiary)] text-[var(--text-secondary)]\">\r\n {badge}\r\n </span>\r\n )}\r\n </button>\r\n ))}\r\n </div>\r\n );\r\n}\r\n\r\n// Sub-component: Tab Content\r\ninterface TabContentProps {\r\n readonly activeTab: TicketTab;\r\n readonly ticket: any;\r\n readonly activities: any[];\r\n readonly isSupport: boolean;\r\n readonly isClosed: boolean;\r\n readonly isReadOnly: boolean;\r\n readonly isConnected: boolean;\r\n readonly currentUserName: string;\r\n readonly saving: boolean;\r\n readonly uploading: boolean;\r\n readonly onAddComment: (content: string, isInternal: boolean) => Promise<void>;\r\n readonly onDownloadAttachment: (attachment: any) => Promise<void>;\r\n readonly onUploadAttachment: (file: File) => Promise<void>;\r\n readonly onStatusChange: (status: any) => Promise<void>;\r\n readonly onAssign?: () => void;\r\n readonly onResolve: () => void;\r\n readonly onClose: () => Promise<void>;\r\n readonly onReopen: () => Promise<void>;\r\n readonly onEscalate: () => void;\r\n readonly getAttachmentUrl: (attachment: any) => string;\r\n readonly onProposeSolution?: (content: string) => Promise<void>;\r\n readonly onApproveSolution?: () => Promise<void>;\r\n readonly onRejectSolution?: (reason: string) => Promise<void>;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction TabContent({\r\n activeTab, ticket, activities, isSupport, isClosed, isReadOnly, isConnected, currentUserName, saving, uploading,\r\n onAddComment, onDownloadAttachment, onUploadAttachment, onStatusChange, onAssign, onResolve, onClose, onReopen, onEscalate, getAttachmentUrl, onProposeSolution, onApproveSolution, onRejectSolution, t\r\n}: TabContentProps) {\r\n if (activeTab === 'description') {\r\n return (\r\n <div className=\"h-full overflow-y-auto p-6 bg-[var(--bg-primary)]\">\r\n {ticket.description ? (\r\n <div\r\n className=\"prose prose-sm max-w-none text-[var(--text-primary)] leading-relaxed [&_a]:text-[var(--color-accent-600)] [&_a]:underline\"\r\n dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(ticket.description) }}\r\n />\r\n ) : (\r\n <p className=\"text-[var(--text-tertiary)] italic\">{t('tabs.noDescription')}</p>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n if (activeTab === 'conversation') {\r\n const ticketVariables = useMemo(() => {\r\n if (!ticket) return undefined;\r\n return {\r\n ticketNumber: ticket.ticketNumber ?? '',\r\n ticketTitle: ticket.title ?? '',\r\n userName: ticket.createdBy?.fullName ?? '',\r\n userFirstName: ticket.createdBy?.fullName?.split(' ')[0] ?? '',\r\n userLastName: ticket.createdBy?.fullName?.split(' ').slice(1).join(' ') ?? '',\r\n userEmail: ticket.createdBy?.email ?? '',\r\n priority: ticket.priority ?? '',\r\n type: ticket.type ?? '',\r\n status: ticket.status ?? '',\r\n agentName: ticket.assignedTo?.fullName ?? '',\r\n agentFirstName: ticket.assignedTo?.fullName?.split(' ')[0] ?? '',\r\n createdAt: ticket.createdAt ? new Date(ticket.createdAt).toLocaleDateString() : '',\r\n resolvedAt: ticket.resolvedAt ? new Date(ticket.resolvedAt).toLocaleDateString() : '',\r\n resolution: ticket.resolution ?? '',\r\n };\r\n }, [ticket]);\r\n\r\n return (\r\n <div className=\"h-full flex flex-col\">\r\n {(ticket.activeSolution || ticket.hasPendingSolution || (isSupport && !isClosed && ticket.status !== 'Closed')) && (\r\n <div className=\"p-4 border-b border-[var(--border-color)]\">\r\n <TicketSolutionPanel\r\n ticketId={ticket.id}\r\n solutions={ticket.solutions ?? []}\r\n activeSolution={ticket.activeSolution}\r\n mode={isSupport ? 'support' : 'client'}\r\n onProposeSolution={!isReadOnly && isSupport ? onProposeSolution : undefined}\r\n onApproveSolution={!isReadOnly ? onApproveSolution : undefined}\r\n onRejectSolution={!isReadOnly ? onRejectSolution : undefined}\r\n isLoading={saving}\r\n />\r\n </div>\r\n )}\r\n <div className=\"flex-1 min-h-0\">\r\n <TicketConversation\r\n ticketId={ticket.id}\r\n comments={ticket.comments}\r\n activities={activities.map(a => ({\r\n id: a.id,\r\n action: a.action as TicketActivityAction,\r\n oldValue: a.oldValue ?? null,\r\n newValue: a.newValue ?? null,\r\n changedByUserName: 'changedByUserName' in a ? a.changedByUserName : null,\r\n createdAt: a.createdAt,\r\n }))}\r\n attachments={ticket.attachments}\r\n isSupport={isSupport}\r\n isClosed={isClosed || isReadOnly}\r\n isConnected={isConnected}\r\n currentUserName={currentUserName}\r\n ticketVariables={ticketVariables}\r\n onAddComment={onAddComment}\r\n onDownloadAttachment={onDownloadAttachment}\r\n getAttachmentUrl={getAttachmentUrl}\r\n />\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n if (activeTab === 'assignment') {\r\n return (\r\n <AssignmentTabContent\r\n ticket={ticket}\r\n canAssign={!isReadOnly && !!onAssign}\r\n onAssign={onAssign}\r\n t={t}\r\n />\r\n );\r\n }\r\n\r\n if (activeTab === 'ai') {\r\n return (\r\n <AiTabContent\r\n ticket={ticket}\r\n t={t}\r\n />\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"h-full overflow-y-auto\">\r\n <div className=\"space-y-6 p-6\">\r\n <TicketSidebar\r\n ticket={ticket as any}\r\n attachments={ticket.attachments}\r\n activities={activities.map(a => ({\r\n action: a.action,\r\n newValue: a.newValue,\r\n createdAt: a.createdAt,\r\n }))}\r\n isSupport={isSupport}\r\n saving={saving}\r\n uploading={uploading}\r\n isReadOnly={isReadOnly}\r\n layout=\"grid\"\r\n onAssign={isReadOnly ? undefined : onAssign}\r\n onStatusChange={isReadOnly ? undefined : onStatusChange}\r\n onResolve={isReadOnly ? undefined : onResolve}\r\n onCloseTicket={isReadOnly ? undefined : onClose}\r\n onReopen={isReadOnly ? undefined : onReopen}\r\n onEscalate={isReadOnly ? undefined : onEscalate}\r\n onDownloadAttachment={onDownloadAttachment}\r\n onUploadAttachment={isReadOnly ? undefined : onUploadAttachment}\r\n getAttachmentUrl={getAttachmentUrl}\r\n />\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// Sub-component: Assignment Tab Content\r\ninterface AssignmentTabContentProps {\r\n readonly ticket: any;\r\n readonly canAssign: boolean;\r\n readonly onAssign?: () => void;\r\n readonly t: TFunction;\r\n}\r\n\r\nfunction AssignmentTabContent({ ticket, canAssign, onAssign, t }: Readonly<AssignmentTabContentProps>) {\r\n const [logs, setLogs] = useState<AssignmentLogDto[]>([]);\r\n const [loadingLogs, setLoadingLogs] = useState(false);\r\n\r\n useEffect(() => {\r\n if (ticket?.id) {\r\n setLoadingLogs(true);\r\n logsApi.getByTicket(ticket.id)\r\n .then(data => setLogs(data))\r\n .catch(() => setLogs([]))\r\n .finally(() => setLoadingLogs(false));\r\n }\r\n }, [ticket?.id]);\r\n\r\n const assignees = ticket.assignees ?? [];\r\n\r\n return (\r\n <div className=\"h-full overflow-y-auto p-6\">\r\n <div className=\"space-y-6\">\r\n {/* Current Assignees */}\r\n <div className=\"card p-4\">\r\n <div className=\"flex items-center justify-between mb-4\">\r\n <h3 className=\"font-semibold text-sm text-[var(--text-primary)]\">\r\n {t('tabs.assignmentCurrentAssignees')}\r\n </h3>\r\n {canAssign && onAssign && (\r\n <button\r\n onClick={onAssign}\r\n className=\"text-xs px-3 py-1.5 rounded-md bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] transition-colors\"\r\n >\r\n {t('tabs.assignmentAssign')}\r\n </button>\r\n )}\r\n </div>\r\n\r\n {assignees.length === 0 ? (\r\n <p className=\"text-sm text-[var(--text-tertiary)] italic\">\r\n {t('tabs.assignmentNoAssignees')}\r\n </p>\r\n ) : (\r\n <div className=\"space-y-2\">\r\n {assignees.map((a: any) => (\r\n <div key={a.userId} className=\"flex items-center gap-3 p-2 rounded-md bg-[var(--bg-secondary)]\">\r\n <div className=\"w-8 h-8 rounded-full bg-[var(--color-accent-100)] text-[var(--color-accent-700)] flex items-center justify-center text-xs font-medium\">\r\n {(a.fullName || a.email || '?').charAt(0).toUpperCase()}\r\n </div>\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"text-sm font-medium text-[var(--text-primary)] truncate\">\r\n {a.fullName || a.email}\r\n </div>\r\n {a.assignedViaGroupName && (\r\n <div className=\"text-xs text-[var(--text-tertiary)]\">\r\n {t('tabs.assignmentViaGroup', { group: a.assignedViaGroupName })}\r\n </div>\r\n )}\r\n </div>\r\n <div className=\"flex items-center gap-2\">\r\n {a.isPrimary && (\r\n <span className=\"text-xs px-2 py-0.5 rounded-full bg-[var(--info-bg)] text-[var(--info-text)]\">\r\n {t('tabs.assignmentPrimary')}\r\n </span>\r\n )}\r\n {a.assignedAt && (\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n {new Date(a.assignedAt).toLocaleDateString()}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Assignment Logs */}\r\n <div className=\"card p-4\">\r\n <h3 className=\"font-semibold text-sm text-[var(--text-primary)] mb-4\">\r\n {t('tabs.assignmentLogs')}\r\n </h3>\r\n\r\n {loadingLogs && (\r\n <div className=\"flex items-center justify-center py-8\">\r\n <Loader2 className=\"w-5 h-5 animate-spin text-[var(--text-tertiary)]\" />\r\n </div>\r\n )}\r\n {!loadingLogs && logs.length === 0 && (\r\n <p className=\"text-sm text-[var(--text-tertiary)] italic\">\r\n {t('tabs.assignmentNoLogs')}\r\n </p>\r\n )}\r\n {!loadingLogs && logs.length > 0 && (\r\n <div className=\"space-y-2\">\r\n {logs.map(log => (\r\n <div key={log.id} className=\"flex items-start gap-3 p-2 rounded-md border border-[var(--border-color)] bg-[var(--bg-primary)]\">\r\n <div className={`mt-0.5 w-2 h-2 rounded-full flex-shrink-0 ${log.success ? 'bg-[var(--success-text)]' : 'bg-[var(--error-text)]'}`} />\r\n <div className=\"flex-1 min-w-0\">\r\n <div className=\"text-sm text-[var(--text-primary)]\">\r\n <span className=\"font-medium\">{log.strategyUsed}</span>\r\n {log.reason && (\r\n <span className=\"text-[var(--text-secondary)]\"> — {log.reason}</span>\r\n )}\r\n </div>\r\n <div className=\"text-xs text-[var(--text-tertiary)] mt-0.5\">\r\n {new Date(log.createdAt).toLocaleString()}\r\n </div>\r\n </div>\r\n <span className={`text-xs px-2 py-0.5 rounded-full ${\r\n log.success\r\n ? 'bg-[var(--success-bg)] text-[var(--success-text)]'\r\n : 'bg-[var(--error-bg)] text-[var(--error-text)]'\r\n }`}>\r\n {log.success ? t('tabs.assignmentSuccess') : t('tabs.assignmentFailed')}\r\n </span>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// Sub-component: AI Tab Content\r\ninterface AiTabContentProps {\r\n readonly ticket: any;\r\n readonly t: TFunction;\r\n}\r\n\r\nconst AI_PROCESSING_TTL = 120_000; // 2 minutes max\r\n\r\nfunction isProcessingActive(key: string): boolean {\r\n const ts = sessionStorage.getItem(key);\r\n if (!ts) return false;\r\n if (Date.now() - Number(ts) > AI_PROCESSING_TTL) {\r\n sessionStorage.removeItem(key);\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\nfunction AiTabContent({ ticket, t }: Readonly<AiTabContentProps>) {\r\n const [cachedData, setCachedData] = useState<import('@/services/api/aiRoutingApi').TicketAiCachedDataDto | null>(null);\r\n const [loadingCached, setLoadingCached] = useState(true);\r\n\r\n const routingKey = `ai_routing_${ticket.id}`;\r\n const summaryKey = `ai_summary_${ticket.id}`;\r\n const [routingProcessing, setRoutingProcessing] = useState(() => isProcessingActive(routingKey));\r\n const [summaryProcessing, setSummaryProcessing] = useState(() => isProcessingActive(summaryKey));\r\n\r\n // Load cached data on mount\r\n useEffect(() => {\r\n let cancelled = false;\r\n const loadCached = async () => {\r\n try {\r\n const { cachedAiApi } = await import('@/services/api/aiRoutingApi');\r\n const data = await cachedAiApi.getCached(ticket.id);\r\n if (!cancelled) {\r\n setCachedData(data);\r\n // Clear processing flags if data already available\r\n if (data.routingSuggestion) {\r\n sessionStorage.removeItem(routingKey);\r\n setRoutingProcessing(false);\r\n }\r\n if (data.summary) {\r\n sessionStorage.removeItem(summaryKey);\r\n setSummaryProcessing(false);\r\n }\r\n }\r\n } catch {\r\n // Silently fail — components will show generate buttons\r\n } finally {\r\n if (!cancelled) setLoadingCached(false);\r\n }\r\n };\r\n loadCached();\r\n return () => { cancelled = true; };\r\n }, [ticket.id, routingKey, summaryKey]);\r\n\r\n // Poll cached data while any processing is active\r\n useEffect(() => {\r\n if (!routingProcessing && !summaryProcessing) return;\r\n let cancelled = false;\r\n const poll = async () => {\r\n try {\r\n const { cachedAiApi } = await import('@/services/api/aiRoutingApi');\r\n const data = await cachedAiApi.getCached(ticket.id);\r\n if (cancelled) return;\r\n setCachedData(data);\r\n if (routingProcessing && data.routingSuggestion) {\r\n sessionStorage.removeItem(routingKey);\r\n setRoutingProcessing(false);\r\n }\r\n if (summaryProcessing && data.summary) {\r\n sessionStorage.removeItem(summaryKey);\r\n setSummaryProcessing(false);\r\n }\r\n } catch {\r\n // Silently ignore poll errors\r\n }\r\n };\r\n const interval = setInterval(poll, 3000);\r\n return () => { cancelled = true; clearInterval(interval); };\r\n }, [routingProcessing, summaryProcessing, ticket.id, routingKey, summaryKey]);\r\n\r\n const onRoutingStart = useCallback(() => {\r\n sessionStorage.setItem(routingKey, Date.now().toString());\r\n setRoutingProcessing(true);\r\n }, [routingKey]);\r\n\r\n const onRoutingEnd = useCallback(() => {\r\n sessionStorage.removeItem(routingKey);\r\n setRoutingProcessing(false);\r\n }, [routingKey]);\r\n\r\n const onSummaryStart = useCallback(() => {\r\n sessionStorage.setItem(summaryKey, Date.now().toString());\r\n setSummaryProcessing(true);\r\n }, [summaryKey]);\r\n\r\n const onSummaryEnd = useCallback(() => {\r\n sessionStorage.removeItem(summaryKey);\r\n setSummaryProcessing(false);\r\n }, [summaryKey]);\r\n\r\n if (loadingCached) {\r\n return (\r\n <div className=\"h-full overflow-y-auto p-6 flex items-center justify-center\">\r\n <Loader2 className=\"w-6 h-6 animate-spin text-[var(--color-primary-600)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"h-full overflow-y-auto p-6\">\r\n <div className=\"space-y-4\">\r\n {/* AI Classification (from ticket entity or cached) */}\r\n {(ticket.aiClassification || cachedData?.classification) && (\r\n <div className=\"card p-4\">\r\n <h3 className=\"font-semibold text-sm mb-3 text-[var(--text-primary)]\">\r\n {t('aiClassification.title')}\r\n </h3>\r\n <AiClassificationBadge classification={ticket.aiClassification || cachedData?.classification} />\r\n </div>\r\n )}\r\n\r\n {/* AI Routing Suggestion */}\r\n <AiRoutingSuggestionPanel\r\n ticketId={ticket.id}\r\n cachedClassification={cachedData?.classification}\r\n cachedRouting={cachedData?.routingSuggestion}\r\n cachedGeneratedAt={cachedData?.routingSuggestionGeneratedAt}\r\n processing={routingProcessing}\r\n onProcessingStart={onRoutingStart}\r\n onProcessingEnd={onRoutingEnd}\r\n />\r\n\r\n {/* AI Summary */}\r\n <AiTicketSummary\r\n ticketId={ticket.id}\r\n cachedSummary={cachedData?.summary}\r\n cachedGeneratedAt={cachedData?.summaryGeneratedAt}\r\n processing={summaryProcessing}\r\n onProcessingStart={onSummaryStart}\r\n onProcessingEnd={onSummaryEnd}\r\n />\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nexport function TicketDetailView({ mode }: TicketDetailViewProps): ReactElement {\r\n const { id: ticketId } = useParams<{ id: string }>();\r\n const navigate = useNavigate();\r\n const { t } = useTranslation('support');\r\n const { user, hasPermission } = useAuth();\r\n const [searchParams, setSearchParams] = useSearchParams();\r\n const { isGlpi } = useTicketingProvider();\r\n const isSupport = mode === 'support';\r\n const currentUserName = getCurrentUserName(isSupport, user);\r\n\r\n\r\n const [activeTab, setActiveTab] = useState<TicketTab>(() => getInitialTabFromUrl(searchParams, VALID_TABS));\r\n\r\n useEffect(() => {\r\n updateTabInUrl(activeTab, searchParams, setSearchParams);\r\n }, [activeTab, searchParams, setSearchParams]);\r\n\r\n const {\r\n ticket, setTicket, loading, setLoading, saving, setSaving, uploading, setUploading,\r\n error, setError, activities, setActivities,\r\n isEditing, setIsEditing, editForm, setEditForm, showAssignModal, setShowAssignModal,\r\n showResolveModal, setShowResolveModal, showEscalateModal, setShowEscalateModal,\r\n resolution, setResolution, escalationLevel, setEscalationLevel,\r\n escalationReason, setEscalationReason, users, setUsers, selectedUserIds, setSelectedUserIds,\r\n selectedGroupIds, setSelectedGroupIds, loadingUsers, setLoadingUsers, userSearchQuery, setUserSearchQuery,\r\n groupSearchQuery, setGroupSearchQuery, groups, setGroups, loadingGroups, setLoadingGroups,\r\n assignModalTab, setAssignModalTab,\r\n satisfaction, setSatisfaction, showRatingForm, setShowRatingForm,\r\n ratingValue, setRatingValue, ratingComment, setRatingComment, submittingRating, setSubmittingRating\r\n } = useTicketDetailState();\r\n\r\n // Load activities\r\n const loadActivities = useCallback(async () => {\r\n if (!ticketId) return;\r\n try {\r\n const data = isSupport\r\n ? await supportApi.tickets.getActivities(ticketId, 50)\r\n : await myTicketsApi.getActivities(ticketId, 50);\r\n setActivities(data);\r\n } catch (err) {\r\n console.error('Failed to load activities', err);\r\n }\r\n }, [ticketId, isSupport]);\r\n\r\n // Load satisfaction (client mode only)\r\n const loadSatisfaction = useCallback(async () => {\r\n if (!ticketId || isSupport) return;\r\n try {\r\n const data = await myTicketsApi.getSatisfaction(ticketId);\r\n setSatisfaction(data);\r\n } catch (err) {\r\n console.error('Failed to load satisfaction', err);\r\n }\r\n }, [ticketId, isSupport]);\r\n\r\n // Load ticket\r\n const loadTicket = useCallback(async () => {\r\n if (!ticketId) return;\r\n try {\r\n setLoading(true);\r\n setError(null);\r\n if (isSupport) {\r\n const data = await supportApi.tickets.getById(ticketId);\r\n if (!data) {\r\n setTicket(null);\r\n return;\r\n }\r\n setTicket(normalizeTicket(data, mode));\r\n setEditForm({ title: data.title, priority: data.priority as TicketPriority });\r\n } else {\r\n const data = await myTicketsApi.getById(ticketId);\r\n if (!data) {\r\n setTicket(null);\r\n return;\r\n }\r\n setTicket(normalizeTicket(data, mode));\r\n loadSatisfaction();\r\n }\r\n loadActivities();\r\n } catch (err) {\r\n setError(t('error.loadFailed', 'Impossible de charger le ticket'));\r\n console.error(err);\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [ticketId, isSupport, mode, loadActivities, loadSatisfaction, t]);\r\n\r\n // SignalR handlers\r\n const handleCommentAdded = useCallback((comment: TicketCommentDto) => {\r\n if (comment.ticketId === ticketId) {\r\n if (!isSupport && comment.isInternal) return;\r\n setTicket(prev => {\r\n if (!prev) return prev;\r\n if (prev.comments.some(c => c.id === comment.id)) return prev;\r\n return {\r\n ...prev,\r\n comments: [...prev.comments, {\r\n id: comment.id,\r\n userName: comment.userName,\r\n content: comment.content,\r\n isInternal: comment.isInternal,\r\n isEdited: comment.isEdited,\r\n createdAt: comment.createdAt,\r\n }],\r\n };\r\n });\r\n }\r\n }, [ticketId, isSupport]);\r\n\r\n const handleTicketUpdated = useCallback((update: TicketUpdateDto) => {\r\n if (update.ticketId === ticketId) {\r\n loadTicket();\r\n }\r\n }, [ticketId, loadTicket]);\r\n\r\n const { isConnected } = useTicketSignalR({\r\n ticketId,\r\n onCommentAdded: handleCommentAdded,\r\n onTicketUpdated: handleTicketUpdated,\r\n });\r\n\r\n useEffect(() => {\r\n loadTicket();\r\n }, [loadTicket]);\r\n\r\n // Load users for assign modal\r\n useEffect(() => {\r\n const loadUsers = async () => {\r\n if (!showAssignModal || users.length > 0 || !isSupport) return;\r\n try {\r\n setLoadingUsers(true);\r\n const data = await api.get<Array<{ id: string; email: string; firstName: string; lastName: string }>>('/api/support/tickets/assignable-users');\r\n const items = Array.isArray(data) ? data : [];\r\n setUsers(items.map(u => ({\r\n id: u.id,\r\n email: u.email,\r\n fullName: `${u.firstName} ${u.lastName}`,\r\n })));\r\n\r\n if ((ticket?.assignees?.length ?? 0) > 0 && ticket?.assignees) {\r\n setSelectedUserIds(ticket.assignees.map(a => a.userId));\r\n }\r\n } catch (err) {\r\n console.error('Failed to load users', err);\r\n } finally {\r\n setLoadingUsers(false);\r\n }\r\n };\r\n loadUsers();\r\n }, [showAssignModal, users.length, isSupport, ticket?.assignees]);\r\n\r\n // Load groups for assign modal\r\n useEffect(() => {\r\n const loadGroups = async () => {\r\n if (!showAssignModal || !isSupport) return;\r\n try {\r\n setLoadingGroups(true);\r\n const data = await api.get<Array<{ id: string; name: string; memberCount: number }>>('/api/support/tickets/assignable-groups');\r\n const items = Array.isArray(data) ? data : [];\r\n setGroups(items.map(g => ({ id: g.id, name: g.name, memberCount: g.memberCount })));\r\n } catch (err) {\r\n console.error('Failed to load groups', err);\r\n } finally {\r\n setLoadingGroups(false);\r\n }\r\n };\r\n loadGroups();\r\n }, [showAssignModal, isSupport]);\r\n\r\n // Handlers\r\n const handleSave = async () => {\r\n if (!ticketId || !isSupport) return;\r\n try {\r\n setSaving(true);\r\n await supportApi.tickets.update(ticketId, { title: editForm.title, priority: editForm.priority, description: ticket?.description || '' });\r\n await loadTicket();\r\n setIsEditing(false);\r\n } catch (err) {\r\n setError(t('error.updateFailed', 'Impossible de mettre à jour le ticket'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleStatusChange = async (status: TicketStatus) => {\r\n if (!ticketId || !isSupport) return;\r\n try {\r\n setSaving(true);\r\n await supportApi.tickets.updateStatus(ticketId, status);\r\n await loadTicket();\r\n } catch (err) {\r\n setError(t('error.statusFailed', 'Impossible de changer le statut'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleAssign = async () => {\r\n if (!ticketId || !isSupport) return;\r\n try {\r\n setSaving(true);\r\n await supportApi.tickets.assign(\r\n ticketId,\r\n selectedUserIds.length > 0 ? selectedUserIds[0] : null,\r\n selectedUserIds.length > 0 ? selectedUserIds : undefined,\r\n selectedGroupIds.length > 0 ? selectedGroupIds : undefined\r\n );\r\n await loadTicket();\r\n setShowAssignModal(false);\r\n setSelectedUserIds([]);\r\n setSelectedGroupIds([]);\r\n setAssignModalTab('users');\r\n } catch {\r\n setError(t('error.assignFailed', 'Impossible d\\'assigner le ticket'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleResolve = async () => {\r\n if (!ticketId || !resolution.trim() || !isSupport) return;\r\n try {\r\n setSaving(true);\r\n await supportApi.tickets.proposeSolution(ticketId, resolution);\r\n await loadTicket();\r\n setShowResolveModal(false);\r\n setResolution('');\r\n } catch {\r\n setError(t('error.resolveFailed', 'Impossible de proposer une solution'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleProposeSolution = async (content: string) => {\r\n if (!ticketId) return;\r\n try {\r\n setSaving(true);\r\n if (isSupport) {\r\n await supportApi.tickets.proposeSolution(ticketId, content);\r\n }\r\n await loadTicket();\r\n } catch {\r\n setError(t('error.resolveFailed', 'Impossible de proposer une solution'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleApproveSolution = async () => {\r\n if (!ticketId) return;\r\n try {\r\n setSaving(true);\r\n if (isSupport) {\r\n await supportApi.tickets.approveSolution(ticketId);\r\n } else {\r\n await myTicketsApi.approveSolution(ticketId);\r\n }\r\n await loadTicket();\r\n } catch {\r\n setError(t('error.approveFailed', 'Impossible d\\'approuver la solution'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleRejectSolution = async (reason: string) => {\r\n if (!ticketId) return;\r\n try {\r\n setSaving(true);\r\n if (isSupport) {\r\n await supportApi.tickets.rejectSolution(ticketId, reason);\r\n } else {\r\n await myTicketsApi.rejectSolution(ticketId, reason);\r\n }\r\n await loadTicket();\r\n } catch {\r\n setError(t('error.rejectFailed', 'Impossible de rejeter la solution'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleClose = async () => {\r\n if (!ticketId || !isSupport) return;\r\n try {\r\n setSaving(true);\r\n await supportApi.tickets.close(ticketId);\r\n await loadTicket();\r\n } catch {\r\n setError(t('error.closeFailed', 'Impossible de fermer le ticket'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleReopen = async () => {\r\n if (!ticketId || !isSupport) return;\r\n try {\r\n setSaving(true);\r\n await supportApi.tickets.reopen(ticketId);\r\n await loadTicket();\r\n } catch {\r\n setError(t('error.reopenFailed', 'Impossible de réouvrir le ticket'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleEscalate = async () => {\r\n if (!ticketId || !isSupport || !escalationLevel) return;\r\n try {\r\n setSaving(true);\r\n await slaApi.escalateTicket(ticketId, escalationLevel, escalationReason || undefined);\r\n await loadTicket();\r\n setShowEscalateModal(false);\r\n setEscalationLevel('Supervisor');\r\n setEscalationReason('');\r\n } catch {\r\n setError(t('error.escalateFailed', 'Failed to escalate ticket'));\r\n } finally {\r\n setSaving(false);\r\n }\r\n };\r\n\r\n const handleAddComment = async (content: string, isInternal: boolean) => {\r\n if (!ticketId || !content.trim()) return;\r\n if (isSupport) {\r\n await supportApi.tickets.addComment(ticketId, { content, isInternal });\r\n } else {\r\n await myTicketsApi.addComment(ticketId, content);\r\n }\r\n await loadTicket();\r\n };\r\n\r\n const handleSubmitRating = async () => {\r\n if (!ticketId || ratingValue === 0 || isSupport) return;\r\n try {\r\n setSubmittingRating(true);\r\n const result = await myTicketsApi.submitSatisfaction(ticketId, {\r\n rating: ratingValue,\r\n comment: ratingComment.trim() || undefined,\r\n });\r\n setSatisfaction(result);\r\n setShowRatingForm(false);\r\n setRatingValue(0);\r\n setRatingComment('');\r\n } catch (err) {\r\n const errorMessage = err instanceof Error ? err.message : t('error.ratingFailed', 'Failed to submit rating');\r\n setError(errorMessage);\r\n } finally {\r\n setSubmittingRating(false);\r\n }\r\n };\r\n\r\n const handleDownloadAttachment = async (attachment: UnifiedAttachment) => {\r\n if (!ticketId) return;\r\n try {\r\n const blob = isSupport\r\n ? await supportApi.tickets.downloadAttachment(ticketId, attachment.id)\r\n : await myTicketsApi.downloadAttachment(ticketId, attachment.id);\r\n const url = window.URL.createObjectURL(blob);\r\n const link = document.createElement('a');\r\n link.href = url;\r\n link.setAttribute('download', attachment.fileName);\r\n document.body.appendChild(link);\r\n link.click();\r\n link.remove();\r\n window.URL.revokeObjectURL(url);\r\n } catch {\r\n setError(t('error.downloadFailed', 'Impossible de télécharger le fichier'));\r\n }\r\n };\r\n\r\n const handleUploadAttachment = async (file: File) => {\r\n if (!ticketId || !isSupport) return;\r\n try {\r\n setUploading(true);\r\n await supportApi.tickets.uploadAttachment(ticketId, file);\r\n await loadTicket();\r\n } catch (err) {\r\n setError(t('error.uploadFailed', 'Failed to upload file'));\r\n throw err;\r\n } finally {\r\n setUploading(false);\r\n }\r\n };\r\n\r\n const getAttachmentUrl = (attachment: UnifiedAttachment): string => {\r\n if (!ticketId) return '';\r\n return isSupport\r\n ? supportApi.tickets.getAttachmentUrl(ticketId, attachment.id)\r\n : `/api/support/my-tickets/${ticketId}/attachments/${attachment.id}`;\r\n };\r\n\r\n // Filter users for assign modal\r\n const filteredUsers = useMemo(() => {\r\n if (!userSearchQuery.trim()) return users;\r\n const query = userSearchQuery.toLowerCase();\r\n return users.filter(\r\n u => u.fullName.toLowerCase().includes(query) || u.email.toLowerCase().includes(query)\r\n );\r\n }, [users, userSearchQuery]);\r\n\r\n // Toggle user selection\r\n const handleToggleUser = useCallback((userId: string) => {\r\n setSelectedUserIds(prev => {\r\n if (prev.includes(userId)) {\r\n return prev.filter(id => id !== userId);\r\n } else {\r\n return [...prev, userId];\r\n }\r\n });\r\n }, []);\r\n\r\n // Toggle group selection\r\n const handleToggleGroup = useCallback((groupId: string) => {\r\n setSelectedGroupIds(prev => {\r\n if (prev.includes(groupId)) {\r\n return prev.filter(id => id !== groupId);\r\n } else {\r\n return [...prev, groupId];\r\n }\r\n });\r\n }, []);\r\n\r\n // Loading state\r\n if (loading) {\r\n return (\r\n <div className=\"flex items-center justify-center h-full\">\r\n <Loader2 className=\"w-8 h-8 animate-spin text-[var(--color-primary-600)]\" />\r\n </div>\r\n );\r\n }\r\n\r\n // Not found\r\n if (!ticket) {\r\n return (\r\n <div className=\"flex flex-col items-center justify-center h-full\">\r\n <AlertTriangle className=\"w-12 h-12 mb-4 text-[var(--text-tertiary)]\" />\r\n <p className=\"text-[var(--text-secondary)] mb-4\">{t('error.notFound', 'Ticket non trouvé')}</p>\r\n <button onClick={() => navigate(-1)} className=\"btn btn-secondary\">\r\n {t('common.goBack', 'Retour')}\r\n </button>\r\n </div>\r\n );\r\n }\r\n\r\n const isClosed = ticket.status === 'Closed' || ticket.status === 'Rejected';\r\n const isReadOnly = isGlpi || !!ticket.isExternallyManaged;\r\n const canAssign = !isReadOnly && hasPermission('support.tickets.assign');\r\n\r\n return (\r\n <div className=\"h-full flex flex-col\">\r\n <TicketHeader\r\n ticket={ticket}\r\n isSupport={isSupport}\r\n isEditing={isEditing}\r\n saving={saving}\r\n editForm={editForm}\r\n error={error}\r\n onEditToggle={setIsEditing}\r\n onSave={handleSave}\r\n onEditFormChange={setEditForm}\r\n onErrorClear={() => setError(null)}\r\n t={t}\r\n />\r\n\r\n <div className=\"flex-shrink-0\">\r\n <TicketInfoPanels\r\n ticket={ticket}\r\n isSupport={isSupport}\r\n satisfaction={satisfaction}\r\n showRatingForm={showRatingForm}\r\n ratingValue={ratingValue}\r\n ratingComment={ratingComment}\r\n submittingRating={submittingRating}\r\n onShowRatingForm={() => setShowRatingForm(true)}\r\n onRatingChange={setRatingValue}\r\n onCommentChange={setRatingComment}\r\n onCancelRating={() => {\r\n setShowRatingForm(false);\r\n setRatingValue(0);\r\n setRatingComment('');\r\n }}\r\n onSubmitRating={handleSubmitRating}\r\n t={t}\r\n />\r\n\r\n <TicketWorkflowHorizontal\r\n currentStatus={ticket.status}\r\n createdAt={ticket.createdAt}\r\n resolvedAt={ticket.resolvedAt}\r\n closedAt={ticket.closedAt}\r\n activities={activities.map(a => ({\r\n action: a.action,\r\n newValue: a.newValue,\r\n createdAt: a.createdAt,\r\n }))}\r\n isSupport={isSupport}\r\n saving={saving}\r\n assignees={ticket.assignees}\r\n onAssign={canAssign ? () => setShowAssignModal(true) : undefined}\r\n onStatusChange={isReadOnly ? undefined : handleStatusChange}\r\n onResolve={isReadOnly ? undefined : () => setShowResolveModal(true)}\r\n onCloseTicket={isReadOnly ? undefined : handleClose}\r\n onReopen={isReadOnly ? undefined : handleReopen}\r\n onEscalate={isReadOnly ? undefined : () => setShowEscalateModal(true)}\r\n />\r\n </div>\r\n\r\n <div className=\"flex-1 flex flex-col min-h-0\">\r\n <TabButtons\r\n activeTab={activeTab}\r\n commentCount={ticket.comments.length}\r\n isSupport={isSupport}\r\n assigneeCount={ticket.assignees?.length ?? 0}\r\n onTabChange={setActiveTab}\r\n t={t}\r\n />\r\n\r\n <div className=\"flex-1 min-h-0\">\r\n <TabContent\r\n activeTab={activeTab}\r\n ticket={ticket}\r\n activities={activities}\r\n isSupport={isSupport}\r\n isClosed={isClosed}\r\n isReadOnly={isReadOnly}\r\n isConnected={isConnected}\r\n currentUserName={currentUserName}\r\n saving={saving}\r\n uploading={uploading}\r\n onAddComment={handleAddComment}\r\n onDownloadAttachment={handleDownloadAttachment}\r\n onUploadAttachment={handleUploadAttachment}\r\n onStatusChange={handleStatusChange}\r\n onAssign={canAssign ? () => setShowAssignModal(true) : undefined}\r\n onResolve={() => setShowResolveModal(true)}\r\n onClose={handleClose}\r\n onReopen={handleReopen}\r\n onEscalate={() => setShowEscalateModal(true)}\r\n getAttachmentUrl={getAttachmentUrl}\r\n onProposeSolution={handleProposeSolution}\r\n onApproveSolution={handleApproveSolution}\r\n onRejectSolution={handleRejectSolution}\r\n t={t}\r\n />\r\n </div>\r\n </div>\r\n\r\n <ResolveModal\r\n show={isSupport && showResolveModal}\r\n resolution={resolution}\r\n saving={saving}\r\n onResolutionChange={setResolution}\r\n onClose={() => setShowResolveModal(false)}\r\n onResolve={handleResolve}\r\n t={t}\r\n />\r\n\r\n <AssignModal\r\n show={isSupport && showAssignModal}\r\n filteredUsers={filteredUsers}\r\n selectedUserIds={selectedUserIds}\r\n selectedGroupIds={selectedGroupIds}\r\n groups={groups}\r\n userSearchQuery={userSearchQuery}\r\n groupSearchQuery={groupSearchQuery}\r\n loadingUsers={loadingUsers}\r\n loadingGroups={loadingGroups}\r\n saving={saving}\r\n activeTab={assignModalTab}\r\n onUserSearchChange={setUserSearchQuery}\r\n onGroupSearchChange={setGroupSearchQuery}\r\n onSelectUser={handleToggleUser}\r\n onSelectGroup={handleToggleGroup}\r\n onTabChange={setAssignModalTab}\r\n onClose={() => {\r\n setShowAssignModal(false);\r\n setSelectedUserIds([]);\r\n setSelectedGroupIds([]);\r\n setAssignModalTab('users');\r\n setUserSearchQuery('');\r\n setGroupSearchQuery('');\r\n }}\r\n onAssign={handleAssign}\r\n t={t}\r\n />\r\n\r\n <EscalateModal\r\n show={isSupport && showEscalateModal}\r\n escalationLevel={escalationLevel}\r\n escalationReason={escalationReason}\r\n saving={saving}\r\n onLevelChange={setEscalationLevel}\r\n onReasonChange={setEscalationReason}\r\n onClose={() => setShowEscalateModal(false)}\r\n onEscalate={handleEscalate}\r\n t={t}\r\n />\r\n </div>\r\n );\r\n}\r\n","import { useState, useEffect, useCallback } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useNavigate } from 'react-router-dom';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Headphones, Search, Filter, Plus, Eye, Trash2, Loader2, FileEdit, Clock, X, ExternalLink } from 'lucide-react';\r\nimport { supportApi, myTicketsApi, type TicketListDto, type MyTicketListDto, type TicketStatsDto, type MyTicketStatsDto, type TicketStatus, type TicketType, type TicketPriority } from '@/services/api/ticketApi';\r\nimport { ViewToggle, StatusBadge, Pagination, KanbanColumn, KanbanCard, type ViewMode } from '@/components/ui/DataView';\r\nimport { ticketDraftService, type TicketDraft } from '@/services/support/ticketDraftService';\r\nimport { useTenant } from '@/contexts/TenantContext';\r\nimport { useTicketingProvider } from '@/hooks/useTicketingProvider';\r\nimport { getTenantBadgeVariant } from '@/utils/notificationTenant';\r\nimport { statusColors } from '@/constants/supportTheme';\r\n\r\nconst VIEW_MODE_STORAGE_KEY = 'support_tickets_view_mode';\r\n\r\n// Valid ticket status transitions (mirrors backend domain state machine)\r\nconst VALID_TRANSITIONS: Record<TicketStatus, TicketStatus[]> = {\r\n Open: ['InProgress', 'OnHold', 'Rejected'],\r\n InProgress: ['OnHold', 'Resolved', 'Rejected'],\r\n OnHold: ['InProgress', 'Resolved', 'Open'],\r\n Resolved: ['Closed', 'Open'],\r\n Closed: ['Open'],\r\n Rejected: ['Open'],\r\n};\r\n\r\nfunction isValidTransition(from: TicketStatus, to: TicketStatus): boolean {\r\n if (from === to) return false;\r\n return VALID_TRANSITIONS[from]?.includes(to) ?? false;\r\n}\r\n\r\nconst priorityColors: Record<string, string> = {\r\n Low: 'var(--info-bg)',\r\n Medium: 'var(--warning-bg)',\r\n High: 'var(--error-bg)',\r\n Critical: 'var(--error-text)',\r\n};\r\n\r\n// Type labels are now handled via translation\r\n\r\ntype TicketItem = TicketListDto | MyTicketListDto;\r\ntype StatsType = TicketStatsDto | MyTicketStatsDto;\r\n\r\nexport interface TicketsListViewProps {\r\n readonly mode: 'admin' | 'assigned' | 'user';\r\n readonly showDrafts?: boolean;\r\n readonly basePath: string;\r\n readonly title: string;\r\n readonly subtitle: string;\r\n readonly createPath: string;\r\n}\r\n\r\nexport function TicketsListView({ mode, showDrafts = false, basePath, title, subtitle, createPath }: TicketsListViewProps): ReactElement {\r\n const { t } = useTranslation(['support', 'admin', 'common']);\r\n const navigate = useNavigate();\r\n const { currentTenant, userTenants, hasMultipleTenants, isGlobalView } = useTenant();\r\n const { isGlpi } = useTicketingProvider();\r\n\r\n const [tickets, setTickets] = useState<TicketItem[]>([]);\r\n const [stats, setStats] = useState<StatsType | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n const [page, setPage] = useState(1);\r\n const [totalPages, setTotalPages] = useState(1);\r\n const [totalCount, setTotalCount] = useState(0);\r\n const [search, setSearch] = useState('');\r\n const [statusFilter, setStatusFilter] = useState<TicketStatus | ''>('');\r\n const [typeFilter, setTypeFilter] = useState<TicketType | ''>('');\r\n const [priorityFilter, setPriorityFilter] = useState<TicketPriority | ''>('');\r\n const [tenantFilter, setTenantFilter] = useState<string>('');\r\n const [viewMode, setViewMode] = useState<ViewMode>(() => {\r\n const saved = localStorage.getItem(VIEW_MODE_STORAGE_KEY);\r\n return (saved as ViewMode) || 'table';\r\n });\r\n const [showFilters, setShowFilters] = useState(false);\r\n const [drafts, setDrafts] = useState<TicketDraft[]>([]);\r\n const [draftsVisible, setDraftsVisible] = useState(true);\r\n // Drag & drop state for Kanban view\r\n const [draggedTicket, setDraggedTicket] = useState<TicketItem | null>(null);\r\n const [dragOverColumn, setDragOverColumn] = useState<TicketStatus | null>(null);\r\n const [dropError, setDropError] = useState<string | null>(null);\r\n\r\n // Show tenant column when admin has multiple tenants or is in global view\r\n const showTenantInfo = (mode === 'admin' || mode === 'assigned') && (hasMultipleTenants || isGlobalView);\r\n\r\n const loadTickets = useCallback(async () => {\r\n try {\r\n setLoading(true);\r\n const params = {\r\n page,\r\n pageSize: 20,\r\n search: search || undefined,\r\n status: statusFilter || undefined,\r\n type: typeFilter || undefined,\r\n priority: priorityFilter || undefined,\r\n tenantId: tenantFilter || undefined,\r\n };\r\n\r\n let result;\r\n if (mode === 'admin') {\r\n result = await supportApi.tickets.getAll(params);\r\n } else if (mode === 'assigned') {\r\n result = await supportApi.tickets.getMyTickets(params);\r\n } else {\r\n result = await myTicketsApi.getAll(params);\r\n }\r\n\r\n setTickets(result?.items ?? []);\r\n setTotalPages(result?.totalPages ?? 1);\r\n setTotalCount(result?.totalCount ?? 0);\r\n } catch (error) {\r\n console.error('Failed to load tickets:', error);\r\n setTickets([]);\r\n setTotalPages(1);\r\n setTotalCount(0);\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [mode, page, search, statusFilter, typeFilter, priorityFilter, tenantFilter]);\r\n\r\n const loadStats = useCallback(async () => {\r\n try {\r\n let result;\r\n if (mode === 'admin') {\r\n result = await supportApi.tickets.getStats(tenantFilter || undefined);\r\n } else if (mode === 'assigned') {\r\n result = await supportApi.tickets.getMyAssignedStats();\r\n } else {\r\n result = await myTicketsApi.getStats();\r\n }\r\n setStats(result);\r\n } catch (error) {\r\n console.error('Failed to load stats:', error);\r\n }\r\n }, [mode, tenantFilter]);\r\n\r\n useEffect(() => {\r\n loadTickets();\r\n }, [loadTickets]);\r\n\r\n useEffect(() => {\r\n loadStats();\r\n }, [loadStats]);\r\n\r\n useEffect(() => {\r\n if (showDrafts) {\r\n const loadedDrafts = ticketDraftService.getAllDrafts();\r\n setDrafts(loadedDrafts);\r\n }\r\n }, [showDrafts]);\r\n\r\n useEffect(() => {\r\n localStorage.setItem(VIEW_MODE_STORAGE_KEY, viewMode);\r\n }, [viewMode]);\r\n\r\n const handleDeleteDraft = (draftId: string) => {\r\n ticketDraftService.deleteDraft(draftId);\r\n setDrafts(ticketDraftService.getAllDrafts());\r\n };\r\n\r\n const handleContinueDraft = (draft: TicketDraft) => {\r\n navigate(`${createPath}?draft=${draft.id}`);\r\n };\r\n\r\n const formatDraftDate = (date: Date) => {\r\n const now = new Date();\r\n const diff = now.getTime() - date.getTime();\r\n const minutes = Math.floor(diff / 60000);\r\n const hours = Math.floor(diff / 3600000);\r\n const days = Math.floor(diff / 86400000);\r\n\r\n if (minutes < 1) return t('common:time.justNow', 'Just now');\r\n if (minutes < 60) return t('common:time.minutesAgo', { count: minutes });\r\n if (hours < 24) return t('common:time.hoursAgo', { count: hours });\r\n return t('common:time.daysAgo', { count: days });\r\n };\r\n\r\n const handleSearch = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setPage(1);\r\n loadTickets();\r\n };\r\n\r\n // Drag and drop handlers for Kanban view\r\n const handleDragStart = (ticket: TicketItem) => {\r\n if (isGlpi) return;\r\n setDraggedTicket(ticket);\r\n };\r\n\r\n const handleDragEnd = () => {\r\n setDraggedTicket(null);\r\n setDragOverColumn(null);\r\n };\r\n\r\n const handleDragOver = (e: React.DragEvent, status: TicketStatus) => {\r\n e.preventDefault();\r\n setDragOverColumn(status);\r\n };\r\n\r\n const handleDragLeave = () => {\r\n setDragOverColumn(null);\r\n };\r\n\r\n const isDropAllowed = (targetStatus: TicketStatus): boolean => {\r\n if (!draggedTicket) return false;\r\n return isValidTransition(draggedTicket.status as TicketStatus, targetStatus);\r\n };\r\n\r\n const handleDrop = async (e: React.DragEvent, newStatus: TicketStatus) => {\r\n e.preventDefault();\r\n setDropError(null);\r\n\r\n if (!draggedTicket || draggedTicket.status === newStatus) {\r\n setDraggedTicket(null);\r\n setDragOverColumn(null);\r\n return;\r\n }\r\n\r\n if (!isValidTransition(draggedTicket.status as TicketStatus, newStatus)) {\r\n setDropError(t('list.invalidTransition', {\r\n from: t(`tickets.statuses.${draggedTicket.status}`),\r\n to: t(`tickets.statuses.${newStatus}`),\r\n }));\r\n setDraggedTicket(null);\r\n setDragOverColumn(null);\r\n setTimeout(() => setDropError(null), 4000);\r\n return;\r\n }\r\n\r\n try {\r\n // Optimistic update\r\n setTickets(prev =>\r\n prev.map(t =>\r\n t.id === draggedTicket.id ? { ...t, status: newStatus } : t\r\n )\r\n );\r\n\r\n // API call to update status\r\n if (mode === 'admin' || mode === 'assigned') {\r\n await supportApi.tickets.updateStatus(draggedTicket.id, newStatus);\r\n }\r\n\r\n loadStats();\r\n loadTickets(); // Refresh from server to confirm update\r\n } catch (error: any) {\r\n console.error('Failed to update ticket status:', error);\r\n const apiMessage = error?.response?.data?.message || error?.response?.data?.detail || '';\r\n setDropError(apiMessage || t('list.statusUpdateFailed'));\r\n setTimeout(() => setDropError(null), 4000);\r\n loadTickets(); // Rollback on error\r\n } finally {\r\n setDraggedTicket(null);\r\n setDragOverColumn(null);\r\n }\r\n };\r\n\r\n const handleDelete = async (ticket: TicketItem) => {\r\n if (!confirm(t('list.confirmDelete', { number: ticket.ticketNumber }))) return;\r\n\r\n try {\r\n await supportApi.tickets.delete(ticket.id);\r\n loadTickets();\r\n loadStats();\r\n } catch (error) {\r\n console.error('Failed to delete ticket:', error);\r\n }\r\n };\r\n\r\n const getDetailPath = (ticketId: string) => `${basePath}/${ticketId}`;\r\n\r\n const renderTenantBadge = (ticket: TicketItem) => {\r\n if (!showTenantInfo) return null;\r\n const tenantId = 'tenantId' in ticket ? (ticket as TicketListDto).tenantId ?? null : null;\r\n const tenantName = 'tenantName' in ticket ? (ticket as TicketListDto).tenantName ?? null : null;\r\n const variant = getTenantBadgeVariant(tenantId, currentTenant?.id ?? null);\r\n\r\n if (variant === 'global') {\r\n return (\r\n <span className=\"text-[10px] px-1.5 py-0.5 rounded bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 border border-purple-200 dark:border-purple-700\">\r\n System\r\n </span>\r\n );\r\n }\r\n if (variant === 'current') {\r\n return (\r\n <span className=\"text-[10px] px-1.5 py-0.5 rounded bg-[var(--bg-secondary)] text-[var(--text-secondary)] border border-[var(--border-color)] truncate max-w-[120px]\">\r\n {tenantName}\r\n </span>\r\n );\r\n }\r\n return (\r\n <span className=\"text-[10px] px-1.5 py-0.5 rounded bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 border border-blue-200 dark:border-blue-700 font-medium truncate max-w-[120px]\">\r\n {tenantName}\r\n </span>\r\n );\r\n };\r\n\r\n const renderStats = () => {\r\n if (!stats) return null;\r\n\r\n const adminStats = stats as TicketStatsDto;\r\n const userStats = stats as MyTicketStatsDto;\r\n\r\n const isAdminOrAssigned = mode === 'admin' || mode === 'assigned';\r\n const statCards = isAdminOrAssigned ? [\r\n { label: t('list.stats.total'), value: adminStats.total, color: 'blue' as const },\r\n { label: t('list.stats.open'), value: adminStats.open, color: 'yellow' as const },\r\n { label: t('list.stats.inProgress'), value: adminStats.inProgress, color: 'blue' as const },\r\n { label: t('list.stats.onHold'), value: adminStats.onHold, color: 'yellow' as const },\r\n { label: t('list.stats.resolved'), value: adminStats.resolved, color: 'green' as const },\r\n { label: t('list.stats.closed'), value: adminStats.closed, color: 'neutral' as const },\r\n { label: t('list.stats.critical'), value: adminStats.critical, color: 'red' as const },\r\n { label: t('list.stats.highPriority'), value: adminStats.highPriority, color: 'yellow' as const },\r\n ] : [\r\n { label: t('list.stats.total'), value: userStats.total, color: 'blue' as const },\r\n { label: t('list.stats.open'), value: userStats.open, color: 'yellow' as const },\r\n { label: t('list.stats.inProgress'), value: userStats.inProgress, color: 'blue' as const },\r\n { label: t('list.stats.resolved'), value: userStats.resolved, color: 'green' as const },\r\n { label: t('list.stats.closed'), value: userStats.closed, color: 'neutral' as const },\r\n ];\r\n\r\n const colorClasses = {\r\n blue: 'bg-[var(--info-bg)] text-[var(--info-text)] border-[var(--info-border)]',\r\n green: 'bg-[var(--success-bg)] text-[var(--success-text)] border-[var(--success-border)]',\r\n yellow: 'bg-[var(--warning-bg)] text-[var(--warning-text)] border-[var(--warning-border)]',\r\n red: 'bg-[var(--error-bg)] text-[var(--error-text)] border-[var(--error-border)]',\r\n neutral: 'bg-[var(--bg-secondary)] text-[var(--text-primary)] border-[var(--border-color)]',\r\n };\r\n\r\n const gridCols = isAdminOrAssigned ? 'lg:grid-cols-8' : 'lg:grid-cols-5';\r\n\r\n return (\r\n <div className={`grid grid-cols-2 md:grid-cols-4 ${gridCols} gap-4 mb-6`}>\r\n {statCards.map((stat) => (\r\n <div key={stat.label} className={`card p-4 text-center border ${colorClasses[stat.color]}`}>\r\n <div className=\"text-2xl font-bold\">{stat.value}</div>\r\n <div className=\"text-sm opacity-80\">{stat.label}</div>\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n };\r\n\r\n const renderTableView = () => (\r\n <div className=\"card overflow-hidden\">\r\n <table className=\"w-full\">\r\n <thead>\r\n <tr className=\"border-b border-[var(--border-color)]\">\r\n <th className=\"text-left p-4 font-medium\">{t('list.table.number')}</th>\r\n {showTenantInfo && (\r\n <th className=\"text-left p-4 font-medium\">{t('list.table.tenant', 'Workspace')}</th>\r\n )}\r\n <th className=\"text-left p-4 font-medium\">{t('list.table.title')}</th>\r\n <th className=\"text-left p-4 font-medium\">{t('list.table.type')}</th>\r\n <th className=\"text-left p-4 font-medium\">{t('list.table.status')}</th>\r\n <th className=\"text-left p-4 font-medium\">{t('list.table.priority')}</th>\r\n <th className=\"text-left p-4 font-medium\">{t('list.table.assignee')}</th>\r\n <th className=\"text-left p-4 font-medium\">{t('list.table.comments')}</th>\r\n <th className=\"text-left p-4 font-medium\">{t('list.table.created')}</th>\r\n <th className=\"text-right p-4 font-medium\">{t('list.table.actions')}</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {tickets.map((ticket) => (\r\n <tr\r\n key={ticket.id}\r\n className=\"border-b border-[var(--border-color)] hover:bg-[var(--bg-hover)] cursor-pointer\"\r\n onClick={() => navigate(getDetailPath(ticket.id))}\r\n >\r\n <td className=\"p-4\">\r\n <span className=\"font-mono text-sm\">{ticket.ticketNumber}</span>\r\n </td>\r\n {showTenantInfo && (\r\n <td className=\"p-4\">\r\n {renderTenantBadge(ticket)}\r\n </td>\r\n )}\r\n <td className=\"p-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <div className=\"font-medium\">{ticket.title}</div>\r\n {ticket.isExternallyManaged && (\r\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-[var(--info-bg)] text-[var(--info-text)] border border-[var(--info-border)]\">\r\n GLPI\r\n </span>\r\n )}\r\n </div>\r\n </td>\r\n <td className=\"p-4\">\r\n <span className=\"text-sm\">{t(`tickets.types.${ticket.type}`, { defaultValue: ticket.type })}</span>\r\n </td>\r\n <td className=\"p-4\">\r\n <StatusBadge status={statusColors[ticket.status] || 'pending'} label={t(`tickets.statuses.${ticket.status}`, { defaultValue: ticket.status })} />\r\n </td>\r\n <td className=\"p-4\">\r\n <span\r\n className=\"px-2 py-1 rounded-[var(--radius-badge)] text-xs font-medium\"\r\n style={{\r\n backgroundColor: priorityColors[ticket.priority],\r\n color: ticket.priority === 'Critical' ? 'white' : 'inherit',\r\n }}\r\n >\r\n {t(`tickets.priorities.${ticket.priority}`, { defaultValue: ticket.priority })}\r\n </span>\r\n </td>\r\n <td className=\"p-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <span className=\"text-sm\">{'assignedToName' in ticket ? (ticket as TicketListDto).assignedToName || '-' : '-'}</span>\r\n {'assigneeCount' in ticket && (ticket as TicketListDto).assigneeCount > 1 && (\r\n <span className=\"ml-1 text-xs px-1.5 py-0.5 rounded-full bg-[var(--bg-tertiary)] text-[var(--text-secondary)]\">\r\n +{(ticket as TicketListDto).assigneeCount - 1}\r\n </span>\r\n )}\r\n </div>\r\n </td>\r\n <td className=\"p-4\">\r\n <span className=\"text-sm\">{ticket.commentCount}</span>\r\n </td>\r\n <td className=\"p-4\">\r\n <span className=\"text-sm text-[var(--text-secondary)]\">\r\n {new Date(ticket.createdAt).toLocaleDateString()}\r\n </span>\r\n </td>\r\n <td className=\"p-4 text-right\" onClick={(e) => e.stopPropagation()}>\r\n <div className=\"flex items-center justify-end gap-2\">\r\n <button\r\n onClick={() => navigate(getDetailPath(ticket.id))}\r\n className=\"btn btn-ghost p-2\"\r\n title={t('list.actions.view')}\r\n >\r\n <Eye className=\"w-4 h-4\" />\r\n </button>\r\n {ticket.externalUrl && (\r\n <a\r\n href={ticket.externalUrl}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"btn btn-ghost p-2 text-[var(--info-text)]\"\r\n title=\"Open in GLPI\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <ExternalLink className=\"w-4 h-4\" />\r\n </a>\r\n )}\r\n {mode === 'admin' && !isGlpi && (\r\n <button\r\n onClick={() => handleDelete(ticket)}\r\n disabled={ticket.isExternallyManaged}\r\n className=\"btn btn-ghost p-2 text-[var(--error-text)] disabled:opacity-50 disabled:cursor-not-allowed\"\r\n title={ticket.isExternallyManaged ? 'Cannot delete externally managed tickets' : t('list.actions.delete')}\r\n >\r\n <Trash2 className=\"w-4 h-4\" />\r\n </button>\r\n )}\r\n </div>\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n\r\n {tickets.length === 0 && !loading && (\r\n <div className=\"p-8 text-center text-[var(--text-secondary)]\">\r\n <Headphones className=\"w-12 h-12 mx-auto mb-4 opacity-50\" />\r\n <p>\r\n {mode === 'admin' && t('list.empty.admin')}\r\n {mode === 'assigned' && t('list.empty.assigned')}\r\n {mode === 'user' && t('list.empty.user')}\r\n </p>\r\n {mode === 'user' && !isGlpi && (\r\n <button\r\n onClick={() => navigate(createPath)}\r\n className=\"btn btn-primary mt-4\"\r\n >\r\n <Plus className=\"w-4 h-4 mr-2\" />\r\n {t('list.empty.createFirst')}\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n\r\n const renderCardsView = () => (\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\r\n {tickets.map((ticket) => (\r\n <button\r\n type=\"button\"\r\n key={ticket.id}\r\n className=\"card p-4 hover:shadow-lg cursor-pointer transition-shadow text-left w-full\"\r\n onClick={() => navigate(getDetailPath(ticket.id))}\r\n >\r\n <div className=\"flex items-start justify-between mb-3\">\r\n <div className=\"flex items-center gap-2\">\r\n <span className=\"font-mono text-sm text-[var(--text-secondary)]\">\r\n {ticket.ticketNumber}\r\n </span>\r\n {renderTenantBadge(ticket)}\r\n </div>\r\n <StatusBadge status={statusColors[ticket.status] || 'pending'} label={t(`tickets.statuses.${ticket.status}`, { defaultValue: ticket.status })} />\r\n </div>\r\n <div className=\"flex items-center gap-2 mb-2\">\r\n <h3 className=\"font-medium line-clamp-2 flex-1\">{ticket.title}</h3>\r\n {ticket.isExternallyManaged && (\r\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-[var(--info-bg)] text-[var(--info-text)] border border-[var(--info-border)] whitespace-nowrap\">\r\n GLPI\r\n </span>\r\n )}\r\n </div>\r\n <div className=\"flex items-center gap-2 mb-3\">\r\n <span className=\"text-xs px-2 py-1 rounded-[var(--radius-badge)] bg-[var(--bg-secondary)]\">\r\n {t(`tickets.types.${ticket.type}`, { defaultValue: ticket.type })}\r\n </span>\r\n <span\r\n className=\"text-xs px-2 py-1 rounded-[var(--radius-badge)]\"\r\n style={{\r\n backgroundColor: priorityColors[ticket.priority],\r\n color: ticket.priority === 'Critical' ? 'white' : 'inherit',\r\n }}\r\n >\r\n {t(`tickets.priorities.${ticket.priority}`, { defaultValue: ticket.priority })}\r\n </span>\r\n </div>\r\n <div className=\"flex items-center justify-between text-sm text-[var(--text-secondary)]\">\r\n <span>{new Date(ticket.createdAt).toLocaleDateString()}</span>\r\n <span>{t('list.comments_other', { count: ticket.commentCount })}</span>\r\n </div>\r\n </button>\r\n ))}\r\n\r\n {tickets.length === 0 && !loading && (\r\n <div className=\"col-span-full card p-12 text-center\">\r\n <Headphones className=\"w-12 h-12 mx-auto mb-4 opacity-50\" />\r\n <p className=\"text-[var(--text-secondary)]\">\r\n {mode === 'admin' && t('list.empty.admin')}\r\n {mode === 'assigned' && t('list.empty.assigned')}\r\n {mode === 'user' && t('list.empty.user')}\r\n </p>\r\n {mode === 'user' && !isGlpi && (\r\n <button\r\n onClick={() => navigate(createPath)}\r\n className=\"btn btn-primary mt-4\"\r\n >\r\n <Plus className=\"w-4 h-4 mr-2\" />\r\n {t('list.empty.createFirst')}\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n\r\n const renderKanbanTicketCard = (ticket: TicketItem) => (\r\n <KanbanCard\r\n key={ticket.id}\r\n onClick={() => navigate(getDetailPath(ticket.id))}\r\n isDragging={draggedTicket?.id === ticket.id}\r\n onDragStart={() => handleDragStart(ticket)}\r\n onDragEnd={handleDragEnd}\r\n >\r\n <div className=\"flex items-center gap-2 text-xs text-[var(--text-secondary)] mb-1\">\r\n <span>{ticket.ticketNumber}</span>\r\n {renderTenantBadge(ticket)}\r\n </div>\r\n <div className=\"font-medium mb-2 line-clamp-2\">{ticket.title}</div>\r\n <div className=\"flex items-center justify-between\">\r\n <span\r\n className=\"text-xs px-2 py-0.5 rounded-[var(--radius-badge)]\"\r\n style={{\r\n backgroundColor: priorityColors[ticket.priority],\r\n color: ticket.priority === 'Critical' ? 'white' : 'inherit',\r\n }}\r\n >\r\n {t(`tickets.priorities.${ticket.priority}`, { defaultValue: ticket.priority })}\r\n </span>\r\n <span className=\"text-xs text-[var(--text-secondary)]\">\r\n {t('list.comments_other', { count: ticket.commentCount })}\r\n </span>\r\n </div>\r\n </KanbanCard>\r\n );\r\n\r\n const renderKanbanView = () => {\r\n const columns: { status: TicketStatus; label: string; color: 'success' | 'warning' | 'error' | 'default' }[] = [\r\n { status: 'Open', label: t('tickets.statuses.Open'), color: 'warning' },\r\n { status: 'InProgress', label: t('tickets.statuses.InProgress'), color: 'default' },\r\n { status: 'OnHold', label: t('tickets.statuses.OnHold'), color: 'warning' },\r\n { status: 'Resolved', label: t('tickets.statuses.Resolved'), color: 'success' },\r\n { status: 'Closed', label: t('tickets.statuses.Closed'), color: 'default' },\r\n ];\r\n\r\n return (\r\n <div className=\"space-y-3\">\r\n {dropError && (\r\n <div className=\"flex items-center gap-3 p-3 rounded-[var(--radius-card)] bg-[var(--error-bg)] border border-[var(--error-border)]\">\r\n <span className=\"text-sm text-[var(--error-text)]\">{dropError}</span>\r\n <button onClick={() => setDropError(null)} className=\"ml-auto text-[var(--error-text)] hover:opacity-70\">\r\n &times;\r\n </button>\r\n </div>\r\n )}\r\n <div className=\"flex gap-4 overflow-x-auto pb-4\">\r\n {columns.map((col) => {\r\n const columnTickets = tickets.filter((t) => t.status === col.status);\r\n const dropDisabled = draggedTicket != null && !isDropAllowed(col.status) && draggedTicket.status !== col.status;\r\n return (\r\n <KanbanColumn\r\n key={col.status}\r\n title={col.label}\r\n count={columnTickets.length}\r\n color={col.color}\r\n isDragOver={dragOverColumn === col.status}\r\n isDropDisabled={dropDisabled}\r\n onDragOver={(e) => handleDragOver(e, col.status)}\r\n onDragLeave={handleDragLeave}\r\n onDrop={(e) => handleDrop(e, col.status)}\r\n >\r\n {columnTickets.map(renderKanbanTicketCard)}\r\n </KanbanColumn>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n );\r\n };\r\n\r\n const renderDraftsSection = () => {\r\n if (!showDrafts || drafts.length === 0) return null;\r\n\r\n return (\r\n <div className=\"card p-4\">\r\n <div className=\"flex items-center justify-between mb-3\">\r\n <div className=\"flex items-center gap-2\">\r\n <FileEdit className=\"w-5 h-5 text-[var(--warning-text)]\" />\r\n <h2 className=\"font-semibold\">{t('common:drafts.title', 'Drafts')}</h2>\r\n <span className=\"px-2 py-0.5 text-xs bg-[var(--warning-bg)] text-[var(--warning-text)] rounded-full\">\r\n {drafts.length}\r\n </span>\r\n </div>\r\n <button\r\n onClick={() => setDraftsVisible(!draftsVisible)}\r\n className=\"btn btn-ghost text-sm\"\r\n >\r\n {draftsVisible ? t('common:actions.hide', 'Hide') : t('common:actions.show', 'Show')}\r\n </button>\r\n </div>\r\n\r\n {draftsVisible && (\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3\">\r\n {drafts.map((draft) => (\r\n <button\r\n type=\"button\"\r\n key={draft.id}\r\n className=\"p-3 bg-[var(--bg-secondary)] rounded-lg border border-[var(--warning-border)] hover:border-[var(--warning-text)] transition-colors cursor-pointer text-left w-full\"\r\n onClick={() => handleContinueDraft(draft)}\r\n >\r\n <div className=\"flex items-start justify-between gap-2\">\r\n <div className=\"flex-1 min-w-0\">\r\n <h3 className=\"font-medium truncate\">\r\n {draft.title || t('common:drafts.untitled', 'Untitled draft')}\r\n </h3>\r\n <div className=\"flex items-center gap-2 mt-1 text-xs text-[var(--text-secondary)] flex-wrap\">\r\n {draft.tenantName && (\r\n <span className=\"px-1.5 py-0.5 bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 rounded truncate max-w-[120px]\">\r\n {draft.tenantName}\r\n </span>\r\n )}\r\n {draft.type && (\r\n <span className=\"px-1.5 py-0.5 bg-[var(--bg-tertiary)] rounded\">\r\n {t(`tickets.types.${draft.type}`, { defaultValue: draft.type })}\r\n </span>\r\n )}\r\n <span className=\"flex items-center gap-1\">\r\n <Clock className=\"w-3 h-3\" />\r\n {formatDraftDate(draft.updatedAt)}\r\n </span>\r\n </div>\r\n </div>\r\n <button\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n handleDeleteDraft(draft.id);\r\n }}\r\n className=\"p-1 hover:bg-[var(--error-bg)] rounded text-[var(--text-secondary)] hover:text-[var(--error-text)]\"\r\n title={t('common:actions.delete', 'Delete')}\r\n >\r\n <X className=\"w-4 h-4\" />\r\n </button>\r\n </div>\r\n </button>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n };\r\n\r\n const getFilterGridCols = () => {\r\n if (mode === 'admin' || mode === 'assigned') {\r\n return showTenantInfo ? 'md:grid-cols-4' : 'md:grid-cols-3';\r\n }\r\n return 'md:grid-cols-2';\r\n };\r\n\r\n return (\r\n <div className=\"space-y-6\">\r\n <div className=\"flex items-center justify-between\">\r\n <div>\r\n <h1 className=\"text-2xl font-bold flex items-center gap-2\">\r\n <Headphones className=\"w-6 h-6\" />\r\n {title}\r\n </h1>\r\n <p className=\"text-[var(--text-secondary)]\">{subtitle}</p>\r\n </div>\r\n {!isGlpi && (\r\n <button\r\n onClick={() => navigate(createPath)}\r\n className=\"btn btn-primary flex items-center gap-2\"\r\n >\r\n <Plus className=\"w-4 h-4\" />\r\n {t('list.newTicket')}\r\n </button>\r\n )}\r\n </div>\r\n\r\n {renderStats()}\r\n\r\n {renderDraftsSection()}\r\n\r\n <div className=\"flex flex-col md:flex-row gap-4\">\r\n <form onSubmit={handleSearch} className=\"flex-1 flex gap-2\">\r\n <div className=\"relative flex-1\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-secondary)]\" />\r\n <input\r\n type=\"text\"\r\n placeholder={t('list.searchPlaceholder')}\r\n value={search}\r\n onChange={(e) => setSearch(e.target.value)}\r\n className=\"input w-full pl-10\"\r\n />\r\n </div>\r\n <button type=\"submit\" className=\"btn btn-secondary\">\r\n {t('list.search')}\r\n </button>\r\n </form>\r\n\r\n <div className=\"flex items-center gap-2\">\r\n <button\r\n onClick={() => setShowFilters(!showFilters)}\r\n className={`btn ${showFilters ? 'btn-primary' : 'btn-secondary'} flex items-center gap-2`}\r\n >\r\n <Filter className=\"w-4 h-4\" />\r\n {t('list.filters')}\r\n </button>\r\n <ViewToggle viewMode={viewMode} onChange={setViewMode} />\r\n </div>\r\n </div>\r\n\r\n {showFilters && (\r\n <div className={`card p-4 grid grid-cols-1 ${getFilterGridCols()} gap-4`}>\r\n {showTenantInfo && (\r\n <div>\r\n <label className=\"block text-sm font-medium mb-1\">{t('list.filter.tenant', 'Workspace')}</label>\r\n <select\r\n value={tenantFilter}\r\n onChange={(e) => {\r\n setTenantFilter(e.target.value);\r\n setPage(1);\r\n }}\r\n className=\"input w-full\"\r\n >\r\n <option value=\"\">{t('list.filter.allTenants', 'All Workspaces')}</option>\r\n {userTenants\r\n .filter(t => t.status === 'Active')\r\n .map(tenant => (\r\n <option key={tenant.id} value={tenant.id}>\r\n {tenant.name}\r\n </option>\r\n ))}\r\n </select>\r\n </div>\r\n )}\r\n <div>\r\n <label className=\"block text-sm font-medium mb-1\">{t('list.filter.status')}</label>\r\n <select\r\n value={statusFilter}\r\n onChange={(e) => {\r\n setStatusFilter(e.target.value as TicketStatus | '');\r\n setPage(1);\r\n }}\r\n className=\"input w-full\"\r\n >\r\n <option value=\"\">{t('list.filter.allStatuses')}</option>\r\n <option value=\"Open\">{t('tickets.statuses.Open')}</option>\r\n <option value=\"InProgress\">{t('tickets.statuses.InProgress')}</option>\r\n <option value=\"OnHold\">{t('tickets.statuses.OnHold')}</option>\r\n <option value=\"Resolved\">{t('tickets.statuses.Resolved')}</option>\r\n <option value=\"Closed\">{t('tickets.statuses.Closed')}</option>\r\n </select>\r\n </div>\r\n <div>\r\n <label className=\"block text-sm font-medium mb-1\">{t('list.filter.type')}</label>\r\n <select\r\n value={typeFilter}\r\n onChange={(e) => {\r\n setTypeFilter(e.target.value as TicketType | '');\r\n setPage(1);\r\n }}\r\n className=\"input w-full\"\r\n >\r\n <option value=\"\">{t('list.filter.allTypes')}</option>\r\n <option value=\"Bug\">{t('tickets.types.Bug')}</option>\r\n <option value=\"FeatureRequest\">{t('tickets.types.FeatureRequest')}</option>\r\n <option value=\"ApplicationRequest\">{t('tickets.types.ApplicationRequest')}</option>\r\n <option value=\"Support\">{t('tickets.types.Support')}</option>\r\n <option value=\"Question\">{t('tickets.types.Question')}</option>\r\n </select>\r\n </div>\r\n {(mode === 'admin' || mode === 'assigned') && (\r\n <div>\r\n <label className=\"block text-sm font-medium mb-1\">{t('list.filter.priority')}</label>\r\n <select\r\n value={priorityFilter}\r\n onChange={(e) => {\r\n setPriorityFilter(e.target.value as TicketPriority | '');\r\n setPage(1);\r\n }}\r\n className=\"input w-full\"\r\n >\r\n <option value=\"\">{t('list.filter.allPriorities')}</option>\r\n <option value=\"Low\">{t('tickets.priorities.Low')}</option>\r\n <option value=\"Medium\">{t('tickets.priorities.Medium')}</option>\r\n <option value=\"High\">{t('tickets.priorities.High')}</option>\r\n <option value=\"Critical\">{t('tickets.priorities.Critical')}</option>\r\n </select>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {loading ? (\r\n <div className=\"flex items-center justify-center py-12\">\r\n <Loader2 className=\"w-8 h-8 animate-spin text-[var(--color-primary-600)]\" />\r\n </div>\r\n ) : (\r\n <>\r\n {viewMode === 'table' && renderTableView()}\r\n {viewMode === 'cards' && renderCardsView()}\r\n {viewMode === 'kanban' && renderKanbanView()}\r\n </>\r\n )}\r\n\r\n {!loading && totalPages > 1 && (\r\n <Pagination\r\n page={page}\r\n totalPages={totalPages}\r\n totalCount={totalCount}\r\n onPageChange={setPage}\r\n />\r\n )}\r\n </div>\r\n );\r\n}\r\n"],"names":["BORDER_RADIUS_VALUES","BORDER_RADIUS_LABELS","ACCENT_COLORS","DEFAULT_BORDER_RADIUS","ITEM_PALETTES","DEFAULT_ITEM_PALETTE","createLightPalette","accentKey","accent","createDarkPalette","hexToRgb","hex","result","DEFAULT_THEME_CONFIG","THEME_PRESETS","THEME_STORAGE_KEY","AppError","message","statusCode","details","NetworkError","AuthenticationError","code","ForbiddenError","MAX_RECENT_ERRORS","LogService","error","component","metadata","baseMessage","displayMessage","apiUrl","status","errorLog","event","category","data","eventLog","resolve","batch","item","token","response","logService","source","lineno","colno","apiClient","axios","config","correlationId","locale","tenantSlug","networkError","authError","requestUrl","isNonCritical","ep","redirectUrl","forbiddenError","bestEffortEndpoints","forbiddenUrl","serverError","appError","api","url","res","userApi","period","errorLimit","limit","withTenantContext","UnlockReason","_createUsersApi","apiContext","params","id","temporaryPassword","userId","sessionId","historyId","tenantId","createUsersApi","_createRolesApi","applicationId","roleId","groupId","languageCode","createRolesApi","_createPermissionsApi","createPermissionsApi","_createApplicationsApi","appId","moduleId","items","accessLevel","request","createApplicationsApi","_createDashboardApi","days","threshold","hours","createDashboardApi","_createSettingsApi","key","value","createSettingsApi","_createPreferencesApi","createPreferencesApi","_createTenantsApi","excludeTypes","rest","searchParams","t","qs","slug","templateId","memberId","toUserId","tenantUserId","roleIds","isActive","sectionId","organisationId","createTenantsApi","_createTemplatesApi","instanceId","createTemplatesApi","_createOrganisationsApi","search","notes","reason","countryCode","identifier","createOrganisationsApi","_createMyTenantsApi","createMyTenantsApi","_createExternalAppsApi","keySlot","page","pageSize","endpointId","createExternalAppsApi","_createApiCatalogApi","createApiCatalogApi","createAdminApi","adminApi","loadLanguageResources","lang","common","navigation","auth","admin","support","docs","ai","communications","entra","structure","preferences","groups","ba","docsAdministrationUsers","docsAdministrationPermissions","docsAdministrationTenants","docsAdministrationAi","docsAdministrationWorkflows","docsAdministrationEntra","docsAdministrationApplications","docsAdministrationConfiguration","docsAdministrationConfigurationLicense","docsAdministrationTenantsTemplate","docsAdministrationTenantsBusiness","docsAdministrationTenantsPersonal","docsAdministrationTenantsOther","docsAdministrationPermissionsAssignments","docsAdministrationTenantsApplications","docsAdministrationTenantsAccessRequests","docsAdministrationTenantsOrganisations","docsPlatformSupportTickets","docsAdministrationUsersDashboard","docsAdministrationUsersReferences","docsAdministrationUsersGroups","__variableDynamicImportRuntimeHelper","resources","detectLanguage","stored","browserLang","detectedLang","i18n","LanguageDetector","initReactI18next","addResources","langResources","ns","translations","initPromise","loads","changeLanguage","ThemeContext","createContext","loadThemeConfig","saved","parsed","prefersDark","serverPrefsToConfig","prefs","accentColorKey","br","applyThemeToDocument","root","palette","itemPalette","itemColors","ThemeProvider","children","setConfig","useState","isSyncing","setIsSyncing","scopeInfo","setScopeInfo","selectedThemeTenant","setSelectedThemeTenantState","tenantOverrides","setTenantOverrides","useTranslation","saveTimeoutRef","useRef","isInitialLoadRef","isThemeScopeInitializedRef","isSwitchingTenantRef","saveToServer","useCallback","newConfig","targetTenantId","effectiveTargetTenantId","currentTenantIdInStorage","prev","debouncedSaveToServer","useLayoutEffect","useEffect","toSave","loadFromServer","forceScope","loadGlobalOnly","serverConfig","applyToTenant","resetToGlobal","loadTenantOverrides","overrides","setSelectedThemeTenant","skipLoad","toggleMode","setMode","mode","setBorderRadius","element","size","setAccentColor","colorKey","setItemPalette","paletteKey","applyPreset","presetConfig","newAccent","resetToDefault","getBorderRadius","jsx","useTheme","context","useContext","cachedConfig","pendingRequest","configApi","signal","FeatureConfigContext","FeatureConfigProvider","isLoading","setIsLoading","controller","err","useFeatureConfig","WILDCARD_PERMISSION","matchesWildcard","wildcardPath","targetPath","wildcardLower","targetLower","prefix","hasPermission","userPermissions","targetPermission","perm","hasFullAccess","permissions","hasRouteAccess","permissionPrefix","prefixDot","p","wildcardBase","AuthContext","TOKEN_KEY","USER_KEY","AuthProvider","user","setUser","login","userData","logout","permission","checkFullAccess","userPerm","refreshPermissions","useAuth","useAuthSafe","hasAllLicenseFeatures","enabledFeatures","LicenseContext","LicenseProvider","license","setLicense","usage","setUsage","setError","abortControllerRef","fetchLicense","licenseData","usageData","isFeatureEnabled","feature","f","refreshLicense","activateLicense","apiKey","useLicense","TenantContext","CURRENT_TENANT_KEY","CURRENT_TENANT_SLUG_KEY","GLOBAL_VIEW_KEY","TenantProvider","isAuthenticated","configLoading","currentTenant","setCurrentTenant","userTenants","setUserTenants","isGlobalView","setIsGlobalView","hasInitialized","currentTenantsKeyRef","b2bTenants","personalTenant","initializeTenants","tenants","hasGlobalRoles","savedTenantId","savedTenant","apiDefaultTenant","firstActive","switchTenant","tenant","enterGlobalView","exitGlobalView","defaultTenant","selectedTenant","setDefaultTenant","clearDefaultTenant","refreshTenants","tenantsKey","a","b","hasGlobal","userHasGlobalRoles","useTenant","MAX_FAVORITES","FavoritesContext","FavoritesProvider","tenantLoading","favorites","setFavorites","loading","setLoading","expandFavorites","setExpandFavorites","triggerExpand","clearExpand","loadFavorites","mapped","dto","handleLanguageChange","addFavorite","module","removeFavorite","isFavorite","toggleFavorite","reorderFavorites","newOrder","limitedOrder","moduleIds","m","previousFavorites","idx","canAddMore","useFavorites","SIDEBAR_STORAGE_KEY","SidebarContext","loadCollapsedState","SidebarProvider","isCollapsed","setIsCollapsed","toggleCollapsed","setCollapsed","collapsed","sidebarWidth","useSidebar","SYSTEM_ROUTE_SEGMENTS","NavigationContext","parseNavigationPath","pathname","parts","NavigationProvider","location","useLocation","menu","setMenu","hasLoadedRef","prevTenantIdRef","refreshVersionRef","contextKey","currentAppCode","currentModuleCode","fetchMenu","fetchFavorites","refreshMenu","version","stripCodePrefix","getRouteSegment","route","segmentIndex","getCurrentApplication","getCurrentModule","getApplicationsByZone","zone","useNavigation","SignalRContext","SignalRProvider","connectionRef","connectionState","setConnectionState","signalR","notificationHandlers","unreadCountHandlers","isRefreshingPermissions","handlePermissionsChanged","connect","connection","retryContext","notification","handler","count","disconnect","onNotification","onUnreadCountUpdate","useSignalRContext","authApi","email","newPassword","password","currentPassword","useSessionMonitor","options","checkIntervalMs","onSessionExpired","onWarningThreshold","enabled","sessionState","setSessionState","isRefreshing","setIsRefreshing","warningShownRef","intervalRef","checkSession","info","minTimeRemaining","refreshSession","handleActivity","events","formatTimeRemaining","seconds","minutes","remainingSeconds","remainingMinutes","SessionWarningModal","isOpen","timeRemainingSeconds","onExtendSession","onLogout","isExtending","setIsExtending","localTimeRemaining","setLocalTimeRemaining","interval","handleExtend","progressPercent","jsxs","Clock","LogOut","RefreshCw","SessionContext","SessionProvider","handleSessionExpired","isWarning","handleLogout","handleExtendSession","TENANT_STABILIZATION_MS","ThemeSync","authContext","authLoading","lastTenantIdRef","hasInitialLoadRef","stabilizationTimeoutRef","pendingTenantIdRef","cancelPendingTimeout","handleGlobalView","startStabilizationTimer","_tenantSlug","handleTenantChange","tenantChanged","hasPendingDifferentTenant","categorizeError","name","generateErrorId","timestamp","random","categoryConfig","WifiOff","ShieldX","FileWarning","AlertTriangle","Bug","ErrorFallback","resetErrorBoundary","copied","setCopied","errorId","Icon","handleGoBack","ArrowLeft","Home","handleError","handleReset","GlobalErrorBoundary","ErrorBoundary","SlotFillContext","useSlotFill","SlotFillProvider","initialFills","fills","useMemo","map","slotName","content","existing","contextValue","c","Slot","fallback","Wrapper","slotContent","props","renderedContent","index","Fragment","finalContent","Fill","slot","registerFill","unregisterFill","renderSlotContent","isValidElement","Component","useSlotHasContent","createSlotFill","TypedSlot","TypedFill","ExtensionContext","useExtensions","usePageOverride","pageKey","getPage","useTableColumnExtensions","tableId","getTableColumns","useFormFieldExtensions","formId","getFormFields","useHasExtension","hasExtension","ExtensionProvider","pages","slots","tableColumns","formFields","PageRegistryClass","PageRegistry","withPageOverride","DefaultComponent","OverridablePage","OverrideComponent","useActivePage","override","entry","ExtendablePage","PAGE_KEYS","lazy","SmartStackContext","SmartStackProvider","DOC_PANEL_STORAGE_KEY","DocPanelContext","loadPanelSize","docMapping","appDocMapping","getDocUrlForPath","segments","offset","appSegments","appCode","moduleParts","i","DocPanelProvider","setIsOpen","panelSize","setPanelSizeState","docUrl","setDocUrl","prevPathnameRef","openDocPanel","targetUrl","closeDocPanel","toggleDocPanel","setPanelSize","useDocPanel","BrowserInfoService","userAgent","match","ntVersion","browserInfoService","useClientContext","browserInfo","recentErrors","useCollapsibleState","collapsedContexts","setCollapsedContexts","collapsedApps","setCollapsedApps","toggleContext","next","toggleApp","appKey","expandAll","collapseAll","contexts","apps","isContextCollapsed","isAppCollapsed","TRACKING_API","EMPTY_GUID","isValidAccessId","accessId","stripTenantPrefix","isApplicationRoute","cleanPath","shouldLogError","ignoredStatuses","endPreviousTracking","startTime","endedAt","durationSeconds","startNewTracking","accessedAt","endTrackingOnUnmount","usePageTracking","accessStartRef","currentAccessIdRef","isTrackingRef","usePermissionMatrix","canEditSystemRoles","saveError","setSaveError","saving","setSaving","saveSuccess","setSaveSuccess","roles","setRoles","setResources","setPermissions","assignments","setAssignments","originalAssignments","setOriginalAssignments","inheritanceMap","setInheritanceMap","filters","setFilters","changesRef","calculateInheritanceForRole","rolePermissions","permissionsMap","targetPermissionId","isDirect","targetPerm","inheritedFromPath","inheritedFromId","shortestWildcardLength","permId","permPath","wildcardPathLength","calculateInheritanceMap","assignmentsMap","roleInheritanceMap","_perm","inheritanceInfo","buildResourceNodes","section","application","permissionsByPath","childResources","nodes","resource","resourcePath","resourcePermissions","resourceNode","buildSectionNodes","childSections","sectionPath","sectionPermissions","resourceNodes","resourcePermissionsCount","sum","r","sectionTotalPermissions","sectionNode","processApplicationModule","modulePath","modulePermissions","sectionNodes","sectionPermissionsCount","s","moduleTotalPermissions","processApplication","flattenPermissionTree","tree","permissionsList","basePath","appCompare","loadData","matrixData","role","permMap","inheritance","originalAssignmentsClone","acc","permSet","saveChanges","changesToSave","updatePromises","permissionIds","clonedData","k","v","newOriginal","errorMessage","scheduleAutoSave","togglePermission","permissionId","rolePerms","grantAllPermissionsForRole","toggleAllForRole","grant","updateRolePermissionsForResource","toggleAllForResource","updated","updateFilters","newFilters","resetChanges","resetAssignments","filteredRoles","filteredResources","hasChanges","perms","original","applications","appSet","modules","categories","categorySet","getPermissionInheritance","roleInheritance","clearSaveError","useSignalR","optionsRef","getToken","handleStorageChange","e","usePermissionSync","isRefreshingRef","isConnected","useTicketSignalR","ticketId","onCommentAdded","onTicketUpdated","onConnectionStateChange","currentTicketRef","joinTicket","leaveTicket","comment","update","isMounted","handleTicketChange","flattenResourceEntries","appIsOpen","modRequiredFeature","entries","flattenSectionEntries","flattenModuleEntries","mod","flattenNavigation","app","useRouteConfig","byKey","byApplication","group","calculateInheritanceForRoleImpl","calculateInheritanceMapImpl","newInheritanceMap","buildPermissionsByPathImpl","getPathPermissionsImpl","normalizedPath","useEntityPermissions","entityId","loadEntity","loadRoles","entity","setEntity","isTenantMode","addModuleNode","addSectionAndResources","processModuleWithSections","entityData","rolesData","relevantRoles","effectivePermissions","effective","_roleId","matchesResourceSearch","matchesApplicationFilter","matchesModuleFilter","getPermissionSources","sources","useUserPermissions","tenantMember","setTenantMember","consolidatedRoles","setConsolidatedRoles","loadUser","loadUserRoles","tid","rolesToUse","consolidated","cr","member","baseResult","userRoles","assignTenantRoles","ThemeSwitcher","isLight","Sun","Moon","languages","LanguageSwitcher","dropdownRef","currentLang","currentLanguage","l","handleSelect","handleClickOutside","isSelected","Check","buildTenantUrl","path","stripSlugFromPath","extractSlugFromPath","useTenantUrl","useUrlRouting","userAvatarGradient","AvatarMenu","navigate","useNavigate","menuRef","handleRefreshCache","handleNavigate","handleEscape","getInitials","first","last","getDisplayName","ChevronDown","LayoutGrid","User","iconMap","Settings","Users","Key","Briefcase","FolderOpen","FileText","Box","getIcon","iconName","AppCard","showFavorite","onToggleFavorite","onSelect","Star","AppSwitcher","hasTenant","handleSelectApp","useTenantFavorites","isFav","newFavorites","recordRecent","statusColors","statusLabels","TenantItem","hasThemeOverride","showStatus","isB2B","isSelectable","getButtonClassName","Building2","RECENT_TENANTS_KEY","MAX_RECENT","getRecentTenantIds","addRecentTenant","recents","setupOutsideClickListener","setSearch","setupEscapeListener","useTenantCategories","filteredTenants","recentIds","favoriteTenants","recentTenants","favSet","otherTenants","excludedIds","TenantSelector","hasMultipleTenants","searchInputRef","handleSelectTenant","handleEnterGlobalView","handleToggleFavorite","searchLower","Globe","Search","Link","Eye","getTenantBadgeVariant","notificationTenantId","currentTenantId","dashboardApi","slaApi","priority","responseBreached","resolutionBreached","warningMinutes","fromDate","toDate","level","notificationsApi","isRead","templatesApi","activeOnly","language","variables","ENTRA_SYNC_TYPES","updateNotificationsList","newNotification","isSyncCompletion","filtered","n","toNotificationDto","useNotifications","unreadCount","setUnreadCount","notifications","setNotifications","supportEnabled","unsubNotification","unsubCount","ignore","fetchCount","loadNotifications","markAsRead","markAllAsRead","deleteNotification","deleted","markAsReadSilent","NotificationBell","setTimerTick","handleMarkAsRead","handleDelete","handleNotificationClick","getNotificationIcon","type","formatElapsedTime","startDateStr","start","now","diffSeconds","formatTime","dateStr","date","diff","renderTenantBadge","variant","Bell","Wifi","CheckCheck","X","Loader2","isActiveSyncNotification","icon","Trash2","variantStyles","Tooltip","position","delay","disabled","className","styles","isVisible","setIsVisible","coords","setCoords","triggerRef","tooltipRef","timeoutRef","calculatePosition","triggerRect","tooltipRect","scrollX","scrollY","gap","top","left","viewportWidth","viewportHeight","padding","showTooltip","hideTooltip","getArrowClasses","base","createPortal","STORAGE_KEY","ErrorContextService","screenshot","dataUrl","domToJpeg","node","tagName","fallbackError","quality","img","canvas","ctx","compressed","json","contextWithoutScreenshot","errors","time","errorContextService","MAX_DRAFTS","EXPIRATION_DAYS","TicketDraftService","d","drafts","storedDrafts","draftsWithoutLargeData","retryError","expirationMs","validDrafts","draft","oldestDraft","updatedDrafts","updates","updatedDraft","success","expiresAt","remainingMs","ticketDraftService","CreateTicketButton","isCapturing","setIsCapturing","showDraftsMenu","setShowDraftsMenu","setDrafts","canCreate","captureAndNavigate","draftId","draftHasScreenshot","capturedContext","handleClick","existingDrafts","handleDeleteDraft","formatDraftTitle","formatTimeAgo","diffMs","diffMins","diffHours","diffDays","renderDraftsMenu","expiration","FileEdit","Plus","Headset","AppHeader","onMenuToggle","isMenuOpen","setIsMenuOpen","allApplications","Menu","Layers","DynamicIcon","IconComponent","LucideIcons","icons","lowerName","matchingKey","FavoriteItem","onClose","buildUrl","isHovered","setIsHovered","tooltipPosition","setTooltipPosition","itemRef","handleMouseEnter","rect","handleMouseLeave","ModuleItem","isExpanded","onToggle","onNavigate","currentPath","flyoutPosition","setFlyoutPosition","expandedSections","setExpandedSections","hasSections","translatedLabel","sectionWithActiveResource","toggleSection","ChevronRight","SectionIcon","sectionActive","SectionItem","hasResources","ResourceItem","findExactMatchModule","currentApp","matchesModuleRoute","findActiveModule","exactMatch","ModuleSidebar","favoritesLoading","useFavoriteModules","expandedModules","setExpandedModules","favoritesCollapsed","setFavoritesCollapsed","resolveFavoriteRoute","favorite","navModule","firstSection","moduleToExpand","toggleModule","appLabel","ChevronLeft","resolvedRoute","MAX_SIZE","DEFAULT_SIZE","DocPanel","baseUrl","separator","fullUrl","isMaximized","handleToggleSize","Minimize2","Maximize2","ExternalLink","ResizableMainContent","groupHandle","setGroupHandle","useGroupCallbackRef","prevPanelSizeRef","isGroupReady","setIsGroupReady","handleResize","defaultLayout","Group","Panel","Separator","DocEdgeButton","BookOpen","LicenseAlertBanner","isReadOnlyMode","dismissed","setDismissed","isSuperAdmin","hasLicensePermission","isTrialExpiringSoon","isExpiringSoon","isError","bannerClasses","Shield","LicenseContentBanner","variantClasses","ctaClasses","AppLayoutContent","sidebarOpen","setSidebarOpen","showSidebar","getSidebarPadding","Outlet","AppLayout","AuthLayout","Header","Footer","currentYear","Github","Twitter","Linkedin","docsIndex","Server","Download","Database","Link2","Monitor","Headphones","Bot","Presentation","DocsSearch","placeholder","onResultClick","query","setQuery","selectedIndex","setSelectedIndex","inputRef","resultsRef","results","searchTerms","searchableText","term","handleKeyDown","navigateToResult","getCategoryLabel","getCategoryColor","ArrowRight","Book","Code","BarChart3","NavItemComponent","isChild","depth","NavLink","DocsLayout","useSearchParams","isEmbedded","isParentActive","child","subChild","isChildActive","renderSubChildren","renderChildren","Sidebar","PublicLayout","Breadcrumb","isLast","isFirst","getNestedValue","obj","part","getAlignmentClass","align","defaultSearchFilter","searchTerm","DataTable","columns","searchable","searchPlaceholder","pagination","onRowClick","getRowKey","_item","emptyMessage","emptyIcon","headerActions","striped","compact","selectable","selectedKeys","onSelectionChange","searchFilter","defaultSortKey","defaultSortDirection","setSearchTerm","sortKey","setSortKey","sortDirection","setSortDirection","currentPage","setCurrentPage","setPageSize","safeData","filteredData","sortedData","aValue","bValue","comparison","paginatedData","totalPages","totalItems","startItem","endItem","handleSort","handlePageChange","handleSelectAll","allKeys","newKeys","handleSelectRow","handleSearchChange","cellPadding","headerPadding","renderSortIcon","column","ChevronUp","ChevronsUpDown","rowKey","globalIndex","ChevronsLeft","ChevronsRight","DataView","controlledViewMode","onViewModeChange","renderTable","renderCards","renderKanban","showTableView","showCardsView","showKanbanView","tableLabel","cardsLabel","kanbanLabel","internalViewMode","setInternalViewMode","viewMode","setViewMode","visibleViews","LayoutList","Columns3","label","ViewToggle","onChange","showTable","showCards","showKanban","views","AVATAR_GRADIENTS","stringToGradient","str","hash","Avatar","sizeClasses","shadowClasses","StatusBadge","showDot","statusStyles","dotColors","RoleBadge","KanbanColumn","title","color","isDragOver","isDropDisabled","onDragOver","onDragLeave","onDrop","colorClasses","KanbanCard","onClick","draggable","isDragging","onDragStart","onDragEnd","Pagination","totalCount","onPageChange","itemLabel","showInfo","showFirstLast","getPageNumbers","end","pageNum","tagVariantStyles","EntityCard","avatar","subtitle","description","stats","badge","links","actions","tags","customHeader","customBody","customFooter","cardClasses","renderAvatar","renderBadge","BadgeIcon","style","renderHeader","renderBody","tag","link","LinkIcon","renderFooter","action","ActionIcon","loadingSpinner","iconElement","ProviderCard","modelCount","websiteUrl","docsUrl","apiKeyUrl","adminApiKeyUrl","hasAdminKey","badgeIcon","badgeTooltip","onGetApiKey","onGetAdminApiKey","labels","websiteLabel","docsLabel","apiKeyLabel","adminApiKeyLabel","modelsLabel","TemplateCard","isSystem","iconColor","translationsCount","onEdit","onDelete","systemLabel","inactiveLabel","activeLabel","editLabel","deleteLabel","KanbanBoard","getItemId","getItemColumn","onMoveItem","renderCard","onAddItem","onCardClick","cardActions","emptyColumnMessage","columnMinWidth","draggedItemId","setDraggedItemId","dragOverColumnId","setDragOverColumnId","activeCardMenu","setActiveCardMenu","handleActionClick","renderActionMenuItem","actionIndex","getColumnItems","columnId","handleDragStart","itemId","handleDragOver","handleDragLeave","handleDrop","handleDragEnd","columnItems","isOverLimit","GripVertical","MoreHorizontal","PageHeader","cellStateConfig","Minus","PermissionMatrix","rows","getRowId","getRowLabel","getRowGroup","getRowSubGroup","getRowIcon","isRowLocked","getRowChildCount","getColumnId","getColumnLabel","getColumnShortLabel","isColumnLocked","getColumnColor","getCellState","onCellClick","showLegend","rowColumnWidth","cellWidth","canEditLocked","collapsedGroups","setCollapsedGroups","groupedRows","row","subGroup","groupData","filterSubGroupsBySearch","subGroups","matchingSubGroups","subRows","matching","filteredGroups","matchingRows","toggleGroup","handleCellClick","rowId","colId","rowLocked","colLocked","isCellEditable","getCellButtonClass","isEditable","state","renderCellButton","col","renderSubGroupRow","rowIndex","rowIcon","bgClass","rowBgClass","Lock","locked","isDefaultGroup","totalRows","arr","childCount","RADIUS_OPTIONS","RadiusSelector","ScopeSelector","ThemeCustomizer","onSync","syncSuccess","showScopeSelector","RotateCcw","preset","Palette","shades","colors","Square","Circle","UnderDevelopment","showBackButton","backUrl","handleBack","Construction","menuItems","LayoutDashboard","AdminSidebar","exact","sidebarContent","active","UserSidebar","dynamicModules","PanelLeft","PanelLeftClose","RouteErrorFallback","handleRouteError","RouteErrorBoundary","FeatureGate","minimal","Sparkles","LicenseActivationDialog","open","onOpenChange","licenseKey","setLicenseKey","setApiKey","showApiKey","setShowApiKey","isActivating","setIsActivating","setSuccess","hasExistingApiKey","handleActivate","handleClose","CheckCircle","AlertCircle","LicenseStatusBadge","showDetails","edition","isInTrial","trialDaysRemaining","daysUntilExpiration","XCircle","urgency","AccessDenied","tenantName","permissionPath","applicationsUrl","relevantPermissions","lower","hasDetails","navigationLabels","appSeg","moduleSeg","sectionSeg","resourceSeg","isUuid","stripPrefix","capitalize","sec","navigationContext","lines","ticketUrl","createUrl","appCode_","subject","fullBody","descriptionText","TicketPlus","NoActiveWorkspace","Mail","PageLoader","ProtectedRoute","navLoading","tenantKey","lastAuditedPath","logAccessDenied","tenantTransitioning","Navigate","TenantRouteWrapper","useParams","isNavigating","pendingSwitchSlug","prevUrlSlugRef","urlSlug","urlSlugChanged","targetTenant","TenantNavigate","to","replace","target","LicenseBlockOverlay","EXEMPT_PREFIXES","LicenseGuard","logicalPath","FeatureRouteGuard","PageTrackerInner","PageTracker","useInRouterContext","BASE_URL","tenantAnalyticsApi","sortBy","useTenantTracking","currentTenantIdRef","TenantTracker","DocsLayout$2","DocsIndexPage","SmartStackPresentationPage","DeveloperIndexPage","InfrastructurePage","InstallationPage","DatabasePage","DualDbContextPage","CrossSchemaFkPage","TenantIndexPage","MultiTenantModePage","TenantB2CPage","TeamsIntegrationPage","SecurityTestingPage","UserIndexPage","ArchitecturePage","ModulesPage","SlaDocPage","SupportIndexPage","SupportDashboardDocPage","SupportTicketsDocPage","SupportMyTicketsDocPage","SupportTemplatesDocPage","SupportEscalationDocPage","AiDocPage","SettingsDocPage","ThemeDocPage","BaIndexPage","BaViewerPage","UsersDocPage","UsersDashboardDocPage","UsersReferencesDocPage","UsersGroupsDocPage","PermissionsDocPage","AssignmentsDocPage","TenantsDocPage","TenantTemplatesDocPage","BusinessTenantsDocPage","PersonalTenantsDocPage","OtherTenantsDocPage","TenantApplicationsDocPage","AccessRequestsDocPage","OrganisationsDocPage","AiAdminDocPage","WorkflowsDocPage","EntraDocPage","ApplicationsDocPage","ConfigurationDocPage","LicenseDocPage","PlatformSupportTicketsDocPage","DocRoutes","Route","OUTLET_SECTIONS","features","HeadphonesIcon","benefits","Zap","Rocket","techStack","HomePage","benefit","tech","extractVersion","pattern","browserDetectors","ua","detectBrowser","detector","windowsVersionMap","getWindowsVersion","osDetectors","_ua","detectOS","detectDeviceTypeFromUa","parseUserAgent","browser","browserVersion","os","osVersion","device","formatted","versionPart","osSuffix","detectDeviceType","getClientIpAddress","useLoginForm","setEmail","setPassword","showPassword","setShowPassword","loadingStep","setLoadingStep","redirectUri","fromUrl","handleSubmit","deviceType","clientIp","themeConfig","getErrorIcon","UserX","KeyRound","getErrorStyle","LoginPage","EyeOff","NotFoundPage","ApplicationsPage","NotificationsPage","PublicLayout$2","AppLayout$2","RegisterPage","ConfirmEmailPage","ForgotPasswordPage","ResetPasswordPage","ForceChangePasswordPage","AuthCallbackPage","OnboardingWizardPage","IMPLICIT_SUFFIXES","getImplicitRoutes","baseKey","baseRoute","routes","suffix","urlPattern","implicitKey","relativePath","buildResourceRoutes","resourceRelRoute","resComponent","ResComponent","implicit","ImplicitComponent","buildSectionRoutes","sectionRelRoute","outletConfig","OutletComponent","tabRoutes","tab","tabKey","TabComponent","secComponent","SecComponent","firstResource","resourceRoutes","buildModuleRoutes","moduleRelRoute","modComponent","ModComponent","sectionRoutes","buildApplicationRoutes","appComponent","AppComponent","firstModule","firstModuleRoute","moduleRoutes","wrapWithFeatureGuard","getStaticAppRoutes","ProtectedCatchAll","CatchAllRoute","DynamicRouter","dynamicAppRoutes","appRoute","appRoutes","wrappedRoutes","staticRoutes","protectedRoutes","Suspense","Routes","getSelectionClass","isDisabled","FavoritesModal","maxFavorites","modulesByApp","handleToggle","sortedSections","defaultRoute","favoriteModule","bugInfo","issueUrl","FavoritesBar","isModalOpen","setIsModalOpen","emptySlots","resolveRoute","handleModuleClick","Settings2","_","ActivityTimeline","activities","RefreshCcw","activity","ActivityEntry","bgColor","getActivityStyle","getActivityDescription","activityStyleMap","PlusCircle","UserPlus","MessageSquare","Paperclip","TrendingUp","Edit","Pause","Play","defaultActivityStyle","getStatusChangedDescription","getPriorityChangedDescription","getAssignedDescription","getCommentedDescription","getAttachmentAddedDescription","getSlaBrachedDescription","getEscalatedDescription","getUpdatedDescription","descriptionMap","descriptionFn","SlaBadge","responseDueAt","resolutionDueAt","firstResponseAt","isPaused","responseSlaPercentage","resolutionSlaPercentage","responseDue","resolutionDue","responseTimeLeft","resolutionTimeLeft","hasResponded","statusColor","StatusIcon","SlaProgressBar","percentage","getColor","ms","remainingHours","categoryColors","SUPPORTED_LANGUAGES","TemplatePicker","ticketVariables","templates","setTemplates","rendering","setRendering","selectedCategory","setSelectedCategory","selectedLanguage","setSelectedLanguage","setCategories","loadTemplates","tpls","cats","tpl","matchesSearch","matchesCategory","rendered","cat","activityConfig","ArrowRightLeft","formatRelativeTime","dateString","formatFileSize","bytes","isImageFile","contentType","getMessageBubbleClasses","isInternal","isOwn","buildTimeline","comments","isSupport","excludeActivities","SystemEvent","getAvatarColor","AuthenticatedImage","src","alt","blobUrl","setBlobUrl","ImageIcon","MessageBubble","attachments","getAttachmentUrl","onDownloadAttachment","previewImage","setPreviewImage","commentTime","relatedAttachments","attachTime","imageAttachments","docAttachments","initials","avatarColor","doc","getTextareaPlaceholder","isInternalComment","getInputAreaClasses","getSendButtonClasses","hasContent","_sending","ConversationHeader","commentCount","EmptyState","TimelineView","timeline","currentUserName","messagesEndRef","InternalNoteToggle","InputArea","newComment","onCommentChange","onKeyDown","onSend","onInternalToggle","sending","textareaRef","onOpenTemplatePicker","Send","ClosedState","TicketConversation","isClosed","onAddComment","setNewComment","setIsInternalComment","setSending","showTemplatePicker","setShowTemplatePicker","visibleCommentCount","handleSend","handleTemplateSelect","getFileType","attachment","ext","imageExts","textExts","videoExts","audioExts","FilePreviewModal","getUrl","onDownload","zoom","setZoom","rotation","setRotation","textContent","setTextContent","isFullscreen","setIsFullscreen","prevAttachmentRef","fileType","currentFileType","currentUrl","abortController","objectUrl","z","handleZoomIn","handleZoomOut","handleRotate","renderContent","File","modal","ZoomOut","ZoomIn","RotateCw","priorityColors","activityIconConfig","UserCheck","ArrowUpCircle","formatActivityDate","MAX_FILE_SIZE","ALLOWED_EXTENSIONS","SectionHeader","TicketSidebar","ticket","uploading","layout","onAssign","onStatusChange","onResolve","onCloseTicket","onReopen","onEscalate","onUploadAttachment","previewFile","setPreviewFile","setIsDragging","uploadError","setUploadError","fileInputRef","isAllowedFile","file","handleFileSelect","files","handleDragEnter","clientContext","isGrid","sectionClass","assignee","Laptop","Smartphone","History","statusOrder","statusConfig","formatShortDate","getStepCircleClasses","step","getBackgroundColor","getStepCircleStyle","getIconColor","getStepLabelClasses","getConnectorColor","StepCircle","StepLabel","StepConnector","TicketWorkflowHorizontal","currentStatus","createdAt","resolvedAt","closedAt","assignees","isRejected","isReopened","steps","currentIndex","statusDates","wasOnHold","statusIdx","isPast","isFuture","onHoldIndex","statusColorMap","statusIconMap","TicketSolutionPanel","_ticketId","solutions","activeSolution","onProposeSolution","onApproveSolution","onRejectSolution","proposing","setProposing","proposedContent","setProposedContent","rejecting","setRejecting","rejectionReason","setRejectionReason","showRejectionModal","setShowRejectionModal","expandedHistory","setExpandedHistory","handleProposeSolution","handleRejectClick","handleSubmitRejection","getAutoCloseCountdown","solution","closeDate","cachedAiApi","analyzeApi","summarizeApi","routingApi","AiClassificationBadge","classification","getConfidenceColor","score","getConfidenceLabel","useAiOperation","setData","abortRef","cancelledRef","execute","executeFn","cancel","reset","AiRoutingSuggestionPanel","cachedClassification","cachedRouting","cachedGeneratedAt","processing","onProcessingStart","onProcessingEnd","initialData","analysis","executeAnalysis","resetAnalysis","applying","setApplying","loadAnalysis","handleApply","errorKey","routing","generatedAt","agent","AiTicketSummary","cachedSummary","executeGenerate","generateSummary","summary","point","blocker","supportApi","assignedToUserId","userIds","groupIds","resolution","attachmentId","formData","myTicketsApi","rulesApi","skillsApi","availabilityApi","logsApi","workloadApi","useTicketingProvider","fetchProvider","VALID_TABS","STATUS_DESCRIPTION_KEYS","SatisfactionRating","satisfaction","showRatingForm","ratingValue","ratingComment","submittingRating","onShowForm","onRatingChange","onCancel","onSubmit","star","ResolveModal","show","onResolutionChange","AssignModal","filteredUsers","selectedUserIds","selectedGroupIds","userSearchQuery","groupSearchQuery","loadingUsers","loadingGroups","activeTab","onUserSearchChange","onGroupSearchChange","onSelectUser","onSelectGroup","onTabChange","g","ESCALATION_LEVEL_VALUES","EscalateModal","escalationLevel","escalationReason","onLevelChange","onReasonChange","normalizeSupportTicket","supportTicket","normalizeClientTicket","clientTicket","normalizeTicket","useTicketDetailState","setTicket","setUploading","setActivities","isEditing","setIsEditing","editForm","setEditForm","showAssignModal","setShowAssignModal","showResolveModal","setShowResolveModal","showEscalateModal","setShowEscalateModal","setResolution","setEscalationLevel","setEscalationReason","users","setUsers","setSelectedUserIds","setSelectedGroupIds","setLoadingUsers","setUserSearchQuery","setGroupSearchQuery","setGroups","setLoadingGroups","assignModalTab","setAssignModalTab","setSatisfaction","setShowRatingForm","setRatingValue","setRatingComment","setSubmittingRating","getCurrentUserName","updateTabInUrl","setSearchParams","currentTab","getInitialTabFromUrl","validTabs","tabParam","TicketHeader","onEditToggle","onSave","onEditFormChange","onErrorClear","Save","Edit2","ShieldAlert","TicketInfoPanels","onShowRatingForm","onCancelRating","onSubmitRating","TabButtons","assigneeCount","tabConfig","Info","TabContent","isReadOnly","DOMPurify","AssignmentTabContent","AiTabContent","canAssign","logs","setLogs","loadingLogs","setLoadingLogs","log","AI_PROCESSING_TTL","isProcessingActive","ts","cachedData","setCachedData","loadingCached","setLoadingCached","routingKey","summaryKey","routingProcessing","setRoutingProcessing","summaryProcessing","setSummaryProcessing","cancelled","aiRoutingApi","onRoutingStart","onRoutingEnd","onSummaryStart","onSummaryEnd","TicketDetailView","isGlpi","setActiveTab","loadActivities","loadSatisfaction","loadTicket","handleCommentAdded","handleTicketUpdated","u","handleSave","handleStatusChange","handleAssign","handleResolve","handleApproveSolution","handleRejectSolution","handleReopen","handleEscalate","handleAddComment","handleSubmitRating","handleDownloadAttachment","blob","handleUploadAttachment","handleToggleUser","handleToggleGroup","VIEW_MODE_STORAGE_KEY","VALID_TRANSITIONS","isValidTransition","from","TicketsListView","showDrafts","createPath","tickets","setTickets","setStats","setPage","setTotalPages","setTotalCount","statusFilter","setStatusFilter","typeFilter","setTypeFilter","priorityFilter","setPriorityFilter","tenantFilter","setTenantFilter","showFilters","setShowFilters","draftsVisible","setDraftsVisible","draggedTicket","setDraggedTicket","dragOverColumn","setDragOverColumn","dropError","setDropError","showTenantInfo","loadTickets","loadStats","loadedDrafts","handleContinueDraft","formatDraftDate","handleSearch","isDropAllowed","targetStatus","newStatus","apiMessage","getDetailPath","renderStats","adminStats","userStats","isAdminOrAssigned","statCards","gridCols","stat","renderTableView","renderCardsView","renderKanbanTicketCard","renderKanbanView","columnTickets","dropDisabled","renderDraftsSection","getFilterGridCols","Filter"],"mappings":"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"}