@atlashub/smartstack 3.21.0 → 3.24.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.
- package/dist/chunks/{AgentSkillsPage-7si3Ng8e.js → AgentSkillsPage-BWQSCYl-.js} +2 -2
- package/dist/chunks/{AgentSkillsPage-7si3Ng8e.js.map → AgentSkillsPage-BWQSCYl-.js.map} +1 -1
- package/dist/chunks/{AgentSkillsPage-D0cD1QdM.js → AgentSkillsPage-IQcMnBaD.js} +2 -2
- package/dist/chunks/{AgentSkillsPage-D0cD1QdM.js.map → AgentSkillsPage-IQcMnBaD.js.map} +1 -1
- package/dist/chunks/{AgentWorkloadPage-H_7ze33H.js → AgentWorkloadPage-DqrjkvWL.js} +2 -2
- package/dist/chunks/{AgentWorkloadPage-H_7ze33H.js.map → AgentWorkloadPage-DqrjkvWL.js.map} +1 -1
- package/dist/chunks/{AgentWorkloadPage-D4d86cdV.js → AgentWorkloadPage-w-HiyFYP.js} +2 -2
- package/dist/chunks/{AgentWorkloadPage-D4d86cdV.js.map → AgentWorkloadPage-w-HiyFYP.js.map} +1 -1
- package/dist/chunks/{ApiCatalogDetailPage-2ktkRrCb.js → ApiCatalogDetailPage-D3L8Yf4G.js} +3 -3
- package/dist/chunks/{ApiCatalogDetailPage-2ktkRrCb.js.map → ApiCatalogDetailPage-D3L8Yf4G.js.map} +1 -1
- package/dist/chunks/{ApiCatalogDetailPage-BQ53xuwD.js → ApiCatalogDetailPage-MPT3Kz6H.js} +2 -2
- package/dist/chunks/{ApiCatalogDetailPage-BQ53xuwD.js.map → ApiCatalogDetailPage-MPT3Kz6H.js.map} +1 -1
- package/dist/chunks/{ApiCatalogPage-BEqTDJz8.js → ApiCatalogPage-D4Hg3uiS.js} +2 -2
- package/dist/chunks/{ApiCatalogPage-BEqTDJz8.js.map → ApiCatalogPage-D4Hg3uiS.js.map} +1 -1
- package/dist/chunks/{ApiCatalogPage-BBkWSLI8.js → ApiCatalogPage-DRg5Cz0r.js} +2 -2
- package/dist/chunks/{ApiCatalogPage-BBkWSLI8.js.map → ApiCatalogPage-DRg5Cz0r.js.map} +1 -1
- package/dist/chunks/{ApplicationDetailPage-BYJ2YMPq.js → ApplicationDetailPage-Caizuyn2.js} +2 -2
- package/dist/chunks/{ApplicationDetailPage-BYJ2YMPq.js.map → ApplicationDetailPage-Caizuyn2.js.map} +1 -1
- package/dist/chunks/{ApplicationDetailPage-D8-bf1as.js → ApplicationDetailPage-CuCW6aMB.js} +4 -4
- package/dist/chunks/{ApplicationDetailPage-D8-bf1as.js.map → ApplicationDetailPage-CuCW6aMB.js.map} +1 -1
- package/dist/chunks/{ApplicationsDashboardPage-BBlLms2r.js → ApplicationsDashboardPage-B2MW8-Kc.js} +2 -2
- package/dist/chunks/{ApplicationsDashboardPage-BBlLms2r.js.map → ApplicationsDashboardPage-B2MW8-Kc.js.map} +1 -1
- package/dist/chunks/{ApplicationsDashboardPage-DTWZxJJM.js → ApplicationsDashboardPage-BDIjFIYZ.js} +3 -3
- package/dist/chunks/{ApplicationsDashboardPage-DTWZxJJM.js.map → ApplicationsDashboardPage-BDIjFIYZ.js.map} +1 -1
- package/dist/chunks/{ApplicationsGridPage-BQaMsK1K.js → ApplicationsGridPage-DV-FihKj.js} +2 -2
- package/dist/chunks/{ApplicationsGridPage-BQaMsK1K.js.map → ApplicationsGridPage-DV-FihKj.js.map} +1 -1
- package/dist/chunks/{ApplicationsGridPage-DbVcvezt.js → ApplicationsGridPage-DXsTfXPI.js} +2 -2
- package/dist/chunks/{ApplicationsGridPage-DbVcvezt.js.map → ApplicationsGridPage-DXsTfXPI.js.map} +1 -1
- package/dist/chunks/{ApplicationsListPage-DYKM2Yeo.js → ApplicationsListPage--CGkyBuJ.js} +2 -2
- package/dist/chunks/{ApplicationsListPage-DYKM2Yeo.js.map → ApplicationsListPage--CGkyBuJ.js.map} +1 -1
- package/dist/chunks/{ApplicationsListPage-C91v2rZt.js → ApplicationsListPage-JUX823bh.js} +2 -2
- package/dist/chunks/{ApplicationsListPage-C91v2rZt.js.map → ApplicationsListPage-JUX823bh.js.map} +1 -1
- package/dist/chunks/{ApplicationsPage-BCbgotIx.js → ApplicationsPage-6zgFye6w.js} +2 -2
- package/dist/chunks/{ApplicationsPage-BCbgotIx.js.map → ApplicationsPage-6zgFye6w.js.map} +1 -1
- package/dist/chunks/{ApplicationsPage-CW3-Hjlu.js → ApplicationsPage-CQPuuiO6.js} +4 -4
- package/dist/chunks/{ApplicationsPage-CW3-Hjlu.js.map → ApplicationsPage-CQPuuiO6.js.map} +1 -1
- package/dist/chunks/{AssignmentRulesPage-D8vfGDBN.js → AssignmentRulesPage-CFffeEbo.js} +2 -2
- package/dist/chunks/{AssignmentRulesPage-D8vfGDBN.js.map → AssignmentRulesPage-CFffeEbo.js.map} +1 -1
- package/dist/chunks/{AssignmentRulesPage-CxktlEMB.js → AssignmentRulesPage-D78UeUId.js} +2 -2
- package/dist/chunks/{AssignmentRulesPage-CxktlEMB.js.map → AssignmentRulesPage-D78UeUId.js.map} +1 -1
- package/dist/chunks/{AssignmentsPage-DmfBYQAD.js → AssignmentsPage-Cww2ifZF.js} +2 -2
- package/dist/chunks/{AssignmentsPage-DmfBYQAD.js.map → AssignmentsPage-Cww2ifZF.js.map} +1 -1
- package/dist/chunks/{AssignmentsPage-sRCCBmRc.js → AssignmentsPage-DE_QS2LO.js} +2 -2
- package/dist/chunks/{AssignmentsPage-sRCCBmRc.js.map → AssignmentsPage-DE_QS2LO.js.map} +1 -1
- package/dist/chunks/{AuthCallbackPage-C7XiZxKb.js → AuthCallbackPage-CA2nO6DG.js} +2 -2
- package/dist/chunks/{AuthCallbackPage-C7XiZxKb.js.map → AuthCallbackPage-CA2nO6DG.js.map} +1 -1
- package/dist/chunks/{AuthCallbackPage-BCe_bwJM.js → AuthCallbackPage-CDUAoX-N.js} +2 -2
- package/dist/chunks/{AuthCallbackPage-BCe_bwJM.js.map → AuthCallbackPage-CDUAoX-N.js.map} +1 -1
- package/dist/chunks/{ConfirmEmailPage-BUfGSqxF.js → ConfirmEmailPage-BqsILAYH.js} +2 -2
- package/dist/chunks/{ConfirmEmailPage-BUfGSqxF.js.map → ConfirmEmailPage-BqsILAYH.js.map} +1 -1
- package/dist/chunks/{ConfirmEmailPage-Buj4x-rx.js → ConfirmEmailPage-INeHCuMB.js} +2 -2
- package/dist/chunks/{ConfirmEmailPage-Buj4x-rx.js.map → ConfirmEmailPage-INeHCuMB.js.map} +1 -1
- package/dist/chunks/{CreateSupportTicketPage-CKDX_HQm.js → CreateSupportTicketPage-BWeuV2aU.js} +2 -2
- package/dist/chunks/{CreateSupportTicketPage-CKDX_HQm.js.map → CreateSupportTicketPage-BWeuV2aU.js.map} +1 -1
- package/dist/chunks/{CreateSupportTicketPage-0LgY-_pu.js → CreateSupportTicketPage-OBwF4v7b.js} +2 -2
- package/dist/chunks/{CreateSupportTicketPage-0LgY-_pu.js.map → CreateSupportTicketPage-OBwF4v7b.js.map} +1 -1
- package/dist/chunks/{DashboardPage-CUZ80NGV.js → DashboardPage-CKHqWrdS.js} +3 -3
- package/dist/chunks/{DashboardPage-CUZ80NGV.js.map → DashboardPage-CKHqWrdS.js.map} +1 -1
- package/dist/chunks/{DashboardPage-CaNOAstg.js → DashboardPage-COmc9b__.js} +3 -3
- package/dist/chunks/{DashboardPage-CaNOAstg.js.map → DashboardPage-COmc9b__.js.map} +1 -1
- package/dist/chunks/{DashboardPage-B48_rQFi.js → DashboardPage-CfKZHiSj.js} +2 -2
- package/dist/chunks/{DashboardPage-B48_rQFi.js.map → DashboardPage-CfKZHiSj.js.map} +1 -1
- package/dist/chunks/{DashboardPage-CUZV1J9t.js → DashboardPage-CwEZZ3jx.js} +2 -2
- package/dist/chunks/{DashboardPage-CUZV1J9t.js.map → DashboardPage-CwEZZ3jx.js.map} +1 -1
- package/dist/chunks/{EscalationConfigPage-CdzAbnGy.js → EscalationConfigPage--7lgZ0kJ.js} +2 -2
- package/dist/chunks/{EscalationConfigPage-CdzAbnGy.js.map → EscalationConfigPage--7lgZ0kJ.js.map} +1 -1
- package/dist/chunks/{EscalationConfigPage-CYGIl_e6.js → EscalationConfigPage-DPyiBcqV.js} +2 -2
- package/dist/chunks/{EscalationConfigPage-CYGIl_e6.js.map → EscalationConfigPage-DPyiBcqV.js.map} +1 -1
- package/dist/chunks/{ForceChangePasswordPage-lRpkwcX7.js → ForceChangePasswordPage-BE-6umub.js} +2 -2
- package/dist/chunks/{ForceChangePasswordPage-lRpkwcX7.js.map → ForceChangePasswordPage-BE-6umub.js.map} +1 -1
- package/dist/chunks/{ForceChangePasswordPage-CvmYAV3r.js → ForceChangePasswordPage-CnsYoWmV.js} +2 -2
- package/dist/chunks/{ForceChangePasswordPage-CvmYAV3r.js.map → ForceChangePasswordPage-CnsYoWmV.js.map} +1 -1
- package/dist/chunks/{ForgotPasswordPage-0u49E4Pw.js → ForgotPasswordPage-CSq4DnFF.js} +2 -2
- package/dist/chunks/{ForgotPasswordPage-0u49E4Pw.js.map → ForgotPasswordPage-CSq4DnFF.js.map} +1 -1
- package/dist/chunks/{ForgotPasswordPage-CxQUqKOm.js → ForgotPasswordPage-DZLVolAC.js} +2 -2
- package/dist/chunks/{ForgotPasswordPage-CxQUqKOm.js.map → ForgotPasswordPage-DZLVolAC.js.map} +1 -1
- package/dist/chunks/{GroupDetailPage-DFBvVO1S.js → GroupDetailPage-Bf9Wb_2j.js} +5 -5
- package/dist/chunks/{GroupDetailPage-DFBvVO1S.js.map → GroupDetailPage-Bf9Wb_2j.js.map} +1 -1
- package/dist/chunks/{GroupDetailPage-B2FkKrGG.js → GroupDetailPage-R-hf3rJ7.js} +2 -2
- package/dist/chunks/{GroupDetailPage-B2FkKrGG.js.map → GroupDetailPage-R-hf3rJ7.js.map} +1 -1
- package/dist/chunks/{MyAccessRequestsPage-C9IX4c0K.js → MyAccessRequestsPage-BIisvWM6.js} +2 -2
- package/dist/chunks/{MyAccessRequestsPage-C9IX4c0K.js.map → MyAccessRequestsPage-BIisvWM6.js.map} +1 -1
- package/dist/chunks/{MyAccessRequestsPage-D6pVULNM.js → MyAccessRequestsPage-BLSV7Tbx.js} +2 -2
- package/dist/chunks/{MyAccessRequestsPage-D6pVULNM.js.map → MyAccessRequestsPage-BLSV7Tbx.js.map} +1 -1
- package/dist/chunks/{MyTenantsPage-BEcYYdGR.js → MyTenantsPage-D-7k9CP1.js} +3 -3
- package/dist/chunks/{MyTenantsPage-BEcYYdGR.js.map → MyTenantsPage-D-7k9CP1.js.map} +1 -1
- package/dist/chunks/{MyTenantsPage-D9f85zjF.js → MyTenantsPage-DqGW6aDt.js} +2 -2
- package/dist/chunks/{MyTenantsPage-D9f85zjF.js.map → MyTenantsPage-DqGW6aDt.js.map} +1 -1
- package/dist/chunks/{MyTicketsPage-DJR8h6y1.js → MyTicketsPage--DgDsnZA.js} +2 -2
- package/dist/chunks/{MyTicketsPage-DJR8h6y1.js.map → MyTicketsPage--DgDsnZA.js.map} +1 -1
- package/dist/chunks/{MyTicketsPage-DiOUExKJ.js → MyTicketsPage-CqJ3Aqob.js} +2 -2
- package/dist/chunks/{MyTicketsPage-DiOUExKJ.js.map → MyTicketsPage-CqJ3Aqob.js.map} +1 -1
- package/dist/chunks/{NavigationAppsPage-CeHbxfZw.js → NavigationAppsPage-Bebis_RT.js} +2 -2
- package/dist/chunks/{NavigationAppsPage-CeHbxfZw.js.map → NavigationAppsPage-Bebis_RT.js.map} +1 -1
- package/dist/chunks/{NavigationAppsPage-If7tmCFY.js → NavigationAppsPage-THNPOAjv.js} +2 -2
- package/dist/chunks/{NavigationAppsPage-If7tmCFY.js.map → NavigationAppsPage-THNPOAjv.js.map} +1 -1
- package/dist/chunks/{NotificationsPage-C29Lln5o.js → NotificationsPage-CAbNW_Cn.js} +2 -2
- package/dist/chunks/{NotificationsPage-C29Lln5o.js.map → NotificationsPage-CAbNW_Cn.js.map} +1 -1
- package/dist/chunks/{NotificationsPage-BiaLRb0s.js → NotificationsPage-DxwizUhL.js} +2 -2
- package/dist/chunks/{NotificationsPage-BiaLRb0s.js.map → NotificationsPage-DxwizUhL.js.map} +1 -1
- package/dist/chunks/{OnboardingWizardPage-DQrBKNBq.js → OnboardingWizardPage-C6HlbJ3K.js} +2 -2
- package/dist/chunks/{OnboardingWizardPage-DQrBKNBq.js.map → OnboardingWizardPage-C6HlbJ3K.js.map} +1 -1
- package/dist/chunks/{OnboardingWizardPage-BQah4cI8.js → OnboardingWizardPage-CyC2zONO.js} +2 -2
- package/dist/chunks/{OnboardingWizardPage-BQah4cI8.js.map → OnboardingWizardPage-CyC2zONO.js.map} +1 -1
- package/dist/chunks/{PermissionDetailPage-Ckjdjvf9.js → PermissionDetailPage-BDHiNgky.js} +2 -2
- package/dist/chunks/{PermissionDetailPage-Ckjdjvf9.js.map → PermissionDetailPage-BDHiNgky.js.map} +1 -1
- package/dist/chunks/{PermissionDetailPage-Dh8v7mGj.js → PermissionDetailPage-C5K17ydY.js} +2 -2
- package/dist/chunks/{PermissionDetailPage-Dh8v7mGj.js.map → PermissionDetailPage-C5K17ydY.js.map} +1 -1
- package/dist/chunks/{PermissionsPage-l0PnY-EE.js → PermissionsPage-COI5LJPo.js} +2 -2
- package/dist/chunks/{PermissionsPage-l0PnY-EE.js.map → PermissionsPage-COI5LJPo.js.map} +1 -1
- package/dist/chunks/{PermissionsPage-DLy9U3P3.js → PermissionsPage-CkOwH2_d.js} +2 -2
- package/dist/chunks/{PermissionsPage-DLy9U3P3.js.map → PermissionsPage-CkOwH2_d.js.map} +1 -1
- package/dist/chunks/{PortalDashboardPage-DFBx38-x.js → PortalDashboardPage-CoEC4CmC.js} +2 -2
- package/dist/chunks/{PortalDashboardPage-DFBx38-x.js.map → PortalDashboardPage-CoEC4CmC.js.map} +1 -1
- package/dist/chunks/{PortalDashboardPage-rQYhrX0q.js → PortalDashboardPage-DrYymEf-.js} +2 -2
- package/dist/chunks/{PortalDashboardPage-rQYhrX0q.js.map → PortalDashboardPage-DrYymEf-.js.map} +1 -1
- package/dist/chunks/{PreferencesPage-BBu8yZQB.js → PreferencesPage-CJRaU3ba.js} +2 -2
- package/dist/chunks/{PreferencesPage-BBu8yZQB.js.map → PreferencesPage-CJRaU3ba.js.map} +1 -1
- package/dist/chunks/{PreferencesPage-B81MsNV1.js → PreferencesPage-Cqr9mAab.js} +2 -2
- package/dist/chunks/{PreferencesPage-B81MsNV1.js.map → PreferencesPage-Cqr9mAab.js.map} +1 -1
- package/dist/chunks/{ProfilePage-DDrl10zj.js → ProfilePage-BZVpg6-l.js} +2 -2
- package/dist/chunks/{ProfilePage-DDrl10zj.js.map → ProfilePage-BZVpg6-l.js.map} +1 -1
- package/dist/chunks/{ProfilePage-DPoXwdnc.js → ProfilePage-Cu_FITeL.js} +2 -2
- package/dist/chunks/{ProfilePage-DPoXwdnc.js.map → ProfilePage-Cu_FITeL.js.map} +1 -1
- package/dist/chunks/{ReferencesManagementPage-eFsKjIEK.js → ReferencesManagementPage-DUlVk9Ps.js} +3 -3
- package/dist/chunks/{ReferencesManagementPage-eFsKjIEK.js.map → ReferencesManagementPage-DUlVk9Ps.js.map} +1 -1
- package/dist/chunks/{ReferencesManagementPage-DhVsuElE.js → ReferencesManagementPage-ZCuYtqd7.js} +2 -2
- package/dist/chunks/{ReferencesManagementPage-DhVsuElE.js.map → ReferencesManagementPage-ZCuYtqd7.js.map} +1 -1
- package/dist/chunks/{RegisterPage-CiQib3-6.js → RegisterPage-C4xmVwh9.js} +2 -2
- package/dist/chunks/{RegisterPage-CiQib3-6.js.map → RegisterPage-C4xmVwh9.js.map} +1 -1
- package/dist/chunks/{RegisterPage-bXCcJD88.js → RegisterPage-DGyzoIHT.js} +2 -2
- package/dist/chunks/{RegisterPage-bXCcJD88.js.map → RegisterPage-DGyzoIHT.js.map} +1 -1
- package/dist/chunks/{ResetPasswordPage-Dqiahhnj.js → ResetPasswordPage-DqDD6VPR.js} +2 -2
- package/dist/chunks/{ResetPasswordPage-Dqiahhnj.js.map → ResetPasswordPage-DqDD6VPR.js.map} +1 -1
- package/dist/chunks/{ResetPasswordPage-CubPG3yv.js → ResetPasswordPage-Glu-aeqv.js} +2 -2
- package/dist/chunks/{ResetPasswordPage-CubPG3yv.js.map → ResetPasswordPage-Glu-aeqv.js.map} +1 -1
- package/dist/chunks/{ResolutionModal-Bg7XZmR1.js → ResolutionModal-CxjANAOP.js} +2 -2
- package/dist/chunks/{ResolutionModal-Bg7XZmR1.js.map → ResolutionModal-CxjANAOP.js.map} +1 -1
- package/dist/chunks/{ResolutionModal-DqRk_T0n.js → ResolutionModal-Duat18qV.js} +2 -2
- package/dist/chunks/{ResolutionModal-DqRk_T0n.js.map → ResolutionModal-Duat18qV.js.map} +1 -1
- package/dist/chunks/{RoleDetailPage-Blau6_4c.js → RoleDetailPage-BQffUSnt.js} +3 -3
- package/dist/chunks/{RoleDetailPage-Blau6_4c.js.map → RoleDetailPage-BQffUSnt.js.map} +1 -1
- package/dist/chunks/{RoleDetailPage-CiRVxxIP.js → RoleDetailPage-JTm5lD1_.js} +2 -2
- package/dist/chunks/{RoleDetailPage-CiRVxxIP.js.map → RoleDetailPage-JTm5lD1_.js.map} +1 -1
- package/dist/chunks/{RolesPage-Pm-RN3lP.js → RolesPage-B9rRzciI.js} +2 -2
- package/dist/chunks/{RolesPage-Pm-RN3lP.js.map → RolesPage-B9rRzciI.js.map} +1 -1
- package/dist/chunks/{RolesPage-Cb8joqdJ.js → RolesPage-BN8_zMOC.js} +2 -2
- package/dist/chunks/{RolesPage-Cb8joqdJ.js.map → RolesPage-BN8_zMOC.js.map} +1 -1
- package/dist/chunks/{SlaConfigPage-B86McKM6.js → SlaConfigPage-B7kZNig4.js} +2 -2
- package/dist/chunks/{SlaConfigPage-B86McKM6.js.map → SlaConfigPage-B7kZNig4.js.map} +1 -1
- package/dist/chunks/{SlaConfigPage-BY7gvYU6.js → SlaConfigPage-okvZfA_K.js} +2 -2
- package/dist/chunks/{SlaConfigPage-BY7gvYU6.js.map → SlaConfigPage-okvZfA_K.js.map} +1 -1
- package/dist/chunks/{SupportPermissionsPage-BYxcLMSd.js → SupportPermissionsPage-DGAPqJbl.js} +2 -2
- package/dist/chunks/{SupportPermissionsPage-BYxcLMSd.js.map → SupportPermissionsPage-DGAPqJbl.js.map} +1 -1
- package/dist/chunks/{SupportPermissionsPage-MXqXNJIZ.js → SupportPermissionsPage-Dg_wLOme.js} +2 -2
- package/dist/chunks/{SupportPermissionsPage-MXqXNJIZ.js.map → SupportPermissionsPage-Dg_wLOme.js.map} +1 -1
- package/dist/chunks/{TemplatesPage-BDguJ401.js → TemplatesPage-DT9fhlAU.js} +2 -2
- package/dist/chunks/{TemplatesPage-BDguJ401.js.map → TemplatesPage-DT9fhlAU.js.map} +1 -1
- package/dist/chunks/{TemplatesPage-DdnGgioU.js → TemplatesPage-DiEk538p.js} +2 -2
- package/dist/chunks/{TemplatesPage-DdnGgioU.js.map → TemplatesPage-DiEk538p.js.map} +1 -1
- package/dist/chunks/{TenantCard-ffwWsgFQ.js → TenantCard-BbSYk9_Z.js} +2 -2
- package/dist/chunks/{TenantCard-ffwWsgFQ.js.map → TenantCard-BbSYk9_Z.js.map} +1 -1
- package/dist/chunks/{TenantCard-CUjb6og9.js → TenantCard-CEkiKxcZ.js} +2 -2
- package/dist/chunks/{TenantCard-CUjb6og9.js.map → TenantCard-CEkiKxcZ.js.map} +1 -1
- package/dist/chunks/{TenantScopeSelector-Dz7i1I43.js → TenantScopeSelector-BWfYxvEa.js} +2 -2
- package/dist/chunks/{TenantScopeSelector-Dz7i1I43.js.map → TenantScopeSelector-BWfYxvEa.js.map} +1 -1
- package/dist/chunks/{TenantScopeSelector-Cym_Zyps.js → TenantScopeSelector-D-BKgQPV.js} +2 -2
- package/dist/chunks/{TenantScopeSelector-Cym_Zyps.js.map → TenantScopeSelector-D-BKgQPV.js.map} +1 -1
- package/dist/chunks/{TicketDetailPage-GOh9GX7E.js → TicketDetailPage-C1mNS9Up.js} +2 -2
- package/dist/chunks/{TicketDetailPage-GOh9GX7E.js.map → TicketDetailPage-C1mNS9Up.js.map} +1 -1
- package/dist/chunks/{TicketDetailPage-Du8WMyqf.js → TicketDetailPage-ieVDRh42.js} +2 -2
- package/dist/chunks/{TicketDetailPage-Du8WMyqf.js.map → TicketDetailPage-ieVDRh42.js.map} +1 -1
- package/dist/chunks/{TicketsPage-Bqd6moQy.js → TicketsPage-CnuWsnIW.js} +2 -2
- package/dist/chunks/{TicketsPage-Bqd6moQy.js.map → TicketsPage-CnuWsnIW.js.map} +1 -1
- package/dist/chunks/{TicketsPage-WdU4Bb7M.js → TicketsPage-jjyY15_D.js} +2 -2
- package/dist/chunks/{TicketsPage-WdU4Bb7M.js.map → TicketsPage-jjyY15_D.js.map} +1 -1
- package/dist/chunks/{UserCreateTicketPage-Cm1emgwR.js → UserCreateTicketPage-B8Tvf-ag.js} +2 -2
- package/dist/chunks/{UserCreateTicketPage-Cm1emgwR.js.map → UserCreateTicketPage-B8Tvf-ag.js.map} +1 -1
- package/dist/chunks/{UserCreateTicketPage-BPw-5Y_D.js → UserCreateTicketPage-DnOsDlfO.js} +2 -2
- package/dist/chunks/{UserCreateTicketPage-BPw-5Y_D.js.map → UserCreateTicketPage-DnOsDlfO.js.map} +1 -1
- package/dist/chunks/{UserDashboardPage-BP5WeXPS.js → UserDashboardPage-BrtkJ-NB.js} +2 -2
- package/dist/chunks/{UserDashboardPage-BP5WeXPS.js.map → UserDashboardPage-BrtkJ-NB.js.map} +1 -1
- package/dist/chunks/{UserDashboardPage-B53C8fUq.js → UserDashboardPage-KLB5CQP5.js} +2 -2
- package/dist/chunks/{UserDashboardPage-B53C8fUq.js.map → UserDashboardPage-KLB5CQP5.js.map} +1 -1
- package/dist/chunks/{UserDetailPage-B110bmGX.js → UserDetailPage-U7smBQoF.js} +5 -5
- package/dist/chunks/{UserDetailPage-B110bmGX.js.map → UserDetailPage-U7smBQoF.js.map} +1 -1
- package/dist/chunks/{UserDetailPage-CV2VCE46.js → UserDetailPage-_J6lcKAU.js} +2 -2
- package/dist/chunks/{UserDetailPage-CV2VCE46.js.map → UserDetailPage-_J6lcKAU.js.map} +1 -1
- package/dist/chunks/{UserTicketDetailPage-CCNJON1V.js → UserTicketDetailPage-CWoYQgH-.js} +2 -2
- package/dist/chunks/{UserTicketDetailPage-CCNJON1V.js.map → UserTicketDetailPage-CWoYQgH-.js.map} +1 -1
- package/dist/chunks/{UserTicketDetailPage-V0mLXrox.js → UserTicketDetailPage-DkufSlvZ.js} +2 -2
- package/dist/chunks/{UserTicketDetailPage-V0mLXrox.js.map → UserTicketDetailPage-DkufSlvZ.js.map} +1 -1
- package/dist/chunks/{UsersGroupsPage-CmdaU-z-.js → UsersGroupsPage-C38s2-Rq.js} +3 -3
- package/dist/chunks/{UsersGroupsPage-CmdaU-z-.js.map → UsersGroupsPage-C38s2-Rq.js.map} +1 -1
- package/dist/chunks/{UsersGroupsPage-BgfAMgEP.js → UsersGroupsPage-Dq3rAteo.js} +2 -2
- package/dist/chunks/{UsersGroupsPage-BgfAMgEP.js.map → UsersGroupsPage-Dq3rAteo.js.map} +1 -1
- package/dist/chunks/{UsersPage-Bg7033pp.js → UsersPage-B5C5KEUR.js} +2 -2
- package/dist/chunks/{UsersPage-Bg7033pp.js.map → UsersPage-B5C5KEUR.js.map} +1 -1
- package/dist/chunks/{UsersPage-TYAfwPY1.js → UsersPage-CXC9Hvq6.js} +2 -2
- package/dist/chunks/{UsersPage-TYAfwPY1.js.map → UsersPage-CXC9Hvq6.js.map} +1 -1
- package/dist/chunks/{accessRequestsApi-DZeDvzwv.js → accessRequestsApi-B-4TJ5_U.js} +2 -2
- package/dist/chunks/{accessRequestsApi-DZeDvzwv.js.map → accessRequestsApi-B-4TJ5_U.js.map} +1 -1
- package/dist/chunks/{accessRequestsApi-ZXFPCid2.js → accessRequestsApi-DZSfThpd.js} +2 -2
- package/dist/chunks/{accessRequestsApi-ZXFPCid2.js.map → accessRequestsApi-DZSfThpd.js.map} +1 -1
- package/dist/chunks/{aiApi-CsH8DXgs.js → aiApi-B20Teu2v.js} +2 -2
- package/dist/chunks/{aiApi-CsH8DXgs.js.map → aiApi-B20Teu2v.js.map} +1 -1
- package/dist/chunks/{aiApi-CVPzFTXa.js → aiApi-DMGz-RPM.js} +2 -2
- package/dist/chunks/{aiApi-CVPzFTXa.js.map → aiApi-DMGz-RPM.js.map} +1 -1
- package/dist/chunks/{applicationAnalyticsApi-B8AhFYLr.js → applicationAnalyticsApi-Bwa75Fzd.js} +2 -2
- package/dist/chunks/{applicationAnalyticsApi-B8AhFYLr.js.map → applicationAnalyticsApi-Bwa75Fzd.js.map} +1 -1
- package/dist/chunks/{applicationAnalyticsApi-Ce_1qOk-.js → applicationAnalyticsApi-CLBqRPfN.js} +2 -2
- package/dist/chunks/{applicationAnalyticsApi-Ce_1qOk-.js.map → applicationAnalyticsApi-CLBqRPfN.js.map} +1 -1
- package/dist/chunks/{groupsApi-BgCk2fsp.js → groupsApi-QzXI-5xu.js} +2 -2
- package/dist/chunks/{groupsApi-BgCk2fsp.js.map → groupsApi-QzXI-5xu.js.map} +1 -1
- package/dist/chunks/{groupsApi-BIbG665N.js → groupsApi-hB9kSWEd.js} +2 -2
- package/dist/chunks/{groupsApi-BIbG665N.js.map → groupsApi-hB9kSWEd.js.map} +1 -1
- package/dist/chunks/{index-Cb3LotuT.js → index--NGcBYUu.js} +3 -3
- package/dist/chunks/{index-Cb3LotuT.js.map → index--NGcBYUu.js.map} +1 -1
- package/dist/chunks/{index-C33zcyF4.js → index--aPwOFjF.js} +2 -2
- package/dist/chunks/{index-C33zcyF4.js.map → index--aPwOFjF.js.map} +1 -1
- package/dist/chunks/{index-sMr9qND_.js → index-0VrOtwP0.js} +2 -2
- package/dist/chunks/{index-sMr9qND_.js.map → index-0VrOtwP0.js.map} +1 -1
- package/dist/chunks/{index-DDKetfKq.js → index-37U271aw.js} +2 -2
- package/dist/chunks/{index-DDKetfKq.js.map → index-37U271aw.js.map} +1 -1
- package/dist/chunks/{index-B9fS7ir6.js → index-B7qZTuQ-.js} +2 -2
- package/dist/chunks/{index-B9fS7ir6.js.map → index-B7qZTuQ-.js.map} +1 -1
- package/dist/chunks/{index-CdjBY7L8.js → index-Bedzmqr-.js} +2 -2
- package/dist/chunks/{index-CdjBY7L8.js.map → index-Bedzmqr-.js.map} +1 -1
- package/dist/chunks/{index-CHG_O1fS.js → index-Betxo5g5.js} +2 -2
- package/dist/chunks/{index-CHG_O1fS.js.map → index-Betxo5g5.js.map} +1 -1
- package/dist/chunks/{index-jiGu-H8x.js → index-BmaJz475.js} +2 -2
- package/dist/chunks/{index-jiGu-H8x.js.map → index-BmaJz475.js.map} +1 -1
- package/dist/chunks/{index-C53JoVNk.js → index-Buhqag3v.js} +2 -2
- package/dist/chunks/{index-C53JoVNk.js.map → index-Buhqag3v.js.map} +1 -1
- package/dist/chunks/{index-DO0Rw7hX.js → index-C3VxlfKq.js} +2 -2
- package/dist/chunks/{index-DO0Rw7hX.js.map → index-C3VxlfKq.js.map} +1 -1
- package/dist/chunks/{index-CSQ60fpG.js → index-CgpRo8Oe.js} +2 -2
- package/dist/chunks/{index-CSQ60fpG.js.map → index-CgpRo8Oe.js.map} +1 -1
- package/dist/chunks/{index-B-e-ELsf.js → index-DOY0w8Iu.js} +8 -8
- package/dist/chunks/{index-B-e-ELsf.js.map → index-DOY0w8Iu.js.map} +1 -1
- package/dist/chunks/{index-D2REDIRX.js → index-DwuvIOrQ.js} +2 -2
- package/dist/chunks/{index-D2REDIRX.js.map → index-DwuvIOrQ.js.map} +1 -1
- package/dist/chunks/{index-DCcl7Qof.js → index-DzedSLdI.js} +2 -2
- package/dist/chunks/{index-DCcl7Qof.js.map → index-DzedSLdI.js.map} +1 -1
- package/dist/chunks/{index-2wUhd9Lu.js → index-IgLVXPg8.js} +10 -10
- package/dist/chunks/index-IgLVXPg8.js.map +1 -0
- package/dist/chunks/{index-CwSaRXXg.js → index-lpIzhufD.js} +1916 -1900
- package/dist/chunks/index-lpIzhufD.js.map +1 -0
- package/dist/chunks/{index-Cuwn2q-f.js → index-mLUKwbGl.js} +4 -4
- package/dist/chunks/{index-Cuwn2q-f.js.map → index-mLUKwbGl.js.map} +1 -1
- package/dist/chunks/{index-B0mk2tNY.js → index-tO6MMIFB.js} +2 -2
- package/dist/chunks/{index-B0mk2tNY.js.map → index-tO6MMIFB.js.map} +1 -1
- package/dist/chunks/{tenantIconMap-BpNANQ5s.js → tenantIconMap-BQD9byc8.js} +2 -2
- package/dist/chunks/{tenantIconMap-BpNANQ5s.js.map → tenantIconMap-BQD9byc8.js.map} +1 -1
- package/dist/chunks/{tenantIconMap-DtdUgvJO.js → tenantIconMap-CTMuSt18.js} +2 -2
- package/dist/chunks/{tenantIconMap-DtdUgvJO.js.map → tenantIconMap-CTMuSt18.js.map} +1 -1
- package/dist/chunks/{ticketingApi-Bu4rKwLl.js → ticketingApi-BNIdox5t.js} +2 -2
- package/dist/chunks/{ticketingApi-Bu4rKwLl.js.map → ticketingApi-BNIdox5t.js.map} +1 -1
- package/dist/chunks/{ticketingApi-r4Avm9iS.js → ticketingApi-J0vC_t7r.js} +2 -2
- package/dist/chunks/{ticketingApi-r4Avm9iS.js.map → ticketingApi-J0vC_t7r.js.map} +1 -1
- package/dist/chunks/{useAccessRequests-B9bF4swg.js → useAccessRequests-DCNNLnxk.js} +3 -3
- package/dist/chunks/{useAccessRequests-B9bF4swg.js.map → useAccessRequests-DCNNLnxk.js.map} +1 -1
- package/dist/chunks/{useAccessRequests-JyPUX3Om.js → useAccessRequests-DT7X4FAK.js} +2 -2
- package/dist/chunks/{useAccessRequests-JyPUX3Om.js.map → useAccessRequests-DT7X4FAK.js.map} +1 -1
- package/dist/chunks/{useUserAccessRequests-DjPQenC2.js → useUserAccessRequests-BYbmG4c7.js} +2 -2
- package/dist/chunks/{useUserAccessRequests-DjPQenC2.js.map → useUserAccessRequests-BYbmG4c7.js.map} +1 -1
- package/dist/chunks/{useUserAccessRequests-BB6FHW14.js → useUserAccessRequests-CylKxRN6.js} +2 -2
- package/dist/chunks/{useUserAccessRequests-BB6FHW14.js.map → useUserAccessRequests-CylKxRN6.js.map} +1 -1
- package/dist/hooks/useCollapsibleState.d.ts +5 -2
- package/dist/hooks/useCollapsibleState.d.ts.map +1 -1
- package/dist/i18n/config.d.ts +1 -0
- package/dist/i18n/config.d.ts.map +1 -1
- package/dist/main.d.ts +0 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/smartstack.cjs +1 -1
- package/dist/smartstack.js +1 -1
- package/dist/utils/permissions.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/index-2wUhd9Lu.js.map +0 -1
- package/dist/chunks/index-CwSaRXXg.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResolutionModal-Bg7XZmR1.js","sources":["../../src/services/api/entraApi.ts","../../src/components/platform/administration/ConflictDiffView.tsx","../../src/hooks/useLocale.ts","../../src/components/platform/administration/entra/ConflictCard.tsx","../../src/components/platform/administration/entra/ResolutionModal.tsx"],"sourcesContent":["import { api, apiClient } from './apiClient';\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) => ({\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 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\n// Types for Entra Dashboard/Sync Status\r\nexport interface EntraSyncStatusDto {\r\n resourceType: string;\r\n status: string;\r\n lastFullSyncAt: string | null;\r\n lastDeltaSyncAt: string | null;\r\n totalItemsSynced: number;\r\n pendingConflicts: number;\r\n lastError: string | null;\r\n}\r\n\r\nexport interface EntraSyncResultDto {\r\n syncSessionId: string;\r\n resourceType: string;\r\n success: boolean;\r\n errorMessage: string | null;\r\n startedAt: string;\r\n completedAt: string;\r\n totalProcessed: number;\r\n created: number;\r\n updated: number;\r\n deleted: number;\r\n skipped: number;\r\n conflicts: number;\r\n pendingConflicts: EntraSyncConflictDto[];\r\n}\r\n\r\nexport interface EntraSyncOptionsDto {\r\n useDeltaSync?: boolean;\r\n syncUsers?: boolean;\r\n syncGroups?: boolean;\r\n syncMemberships?: boolean;\r\n conflictPriority?: 'ManualReview' | 'EntraWins' | 'LocalWins';\r\n autoCreateReferences?: boolean;\r\n}\r\n\r\n// Types for Entra Sync History\r\nexport interface EntraSyncSessionDto {\r\n id: string;\r\n resourceType: string;\r\n isFullSync: boolean;\r\n status: string;\r\n startedAt: string;\r\n completedAt: string | null;\r\n errorMessage: string | null;\r\n totalProcessed: number;\r\n created: number;\r\n updated: number;\r\n deleted: number;\r\n skipped: number;\r\n conflicts: number;\r\n triggerType: string;\r\n triggeredByUserId: string | null;\r\n triggeredByUserName: string | null;\r\n duration: string | null;\r\n}\r\n\r\nexport interface EntraSyncHistoryResponse {\r\n sessions: EntraSyncSessionDto[];\r\n totalCount: number;\r\n page: number;\r\n pageSize: number;\r\n}\r\n\r\n// Types for Entra Conflicts\r\nexport interface ConflictDataDto {\r\n firstName: string | null;\r\n lastName: string | null;\r\n displayName: string | null;\r\n email: string | null;\r\n phoneNumber: string | null;\r\n mobilePhone: string | null;\r\n department: string | null;\r\n jobTitle: string | null;\r\n}\r\n\r\nexport interface EntraSyncConflictDto {\r\n id: string;\r\n resourceType: string;\r\n entraObjectId: string;\r\n entraDisplayName: string;\r\n entraEmail: string | null;\r\n existingLocalEntityId: string | null;\r\n existingLocalDisplayName: string | null;\r\n conflictType: string;\r\n conflictDescription: string;\r\n resolution: string;\r\n createdAt: string;\r\n entraData: ConflictDataDto | null;\r\n localData: ConflictDataDto | null;\r\n}\r\n\r\nexport interface ResolveConflictRequest {\r\n resolution: 'EntraPriority' | 'LocalPriority' | 'SkipImport' | 'MergeToExisting' | 'CreateNew';\r\n notes?: string;\r\n}\r\n\r\n// Types for Entra Groups\r\nexport interface EntraGroupDto {\r\n id: string;\r\n entraObjectId: string;\r\n displayName: string;\r\n description: string | null;\r\n mail: string | null;\r\n isSecurityGroup: boolean;\r\n isActive: boolean;\r\n lastSyncedAt: string;\r\n memberCount: number;\r\n assignedRoles: EntraGroupRoleDto[];\r\n /** Roles inherited from parent groups in the hierarchy */\r\n inheritedRoles: InheritedRoleDto[];\r\n // Parent-child relationship for nested groups\r\n parentGroupId: string | null;\r\n parentGroupName: string | null;\r\n childGroupCount: number;\r\n childGroups: EntraGroupSummaryDto[];\r\n}\r\n\r\nexport interface EntraGroupSummaryDto {\r\n id: string;\r\n entraObjectId: string;\r\n displayName: string;\r\n isSecurityGroup: boolean;\r\n memberCount: number;\r\n childGroupCount: number;\r\n}\r\n\r\nexport interface EntraGroupRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n assignedAt: string;\r\n assignedBy: string;\r\n /** If true, this role is inherited by child groups in the hierarchy */\r\n isInheritable: boolean;\r\n}\r\n\r\n/**\r\n * Represents a role inherited from a parent group in the hierarchy\r\n */\r\nexport interface InheritedRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n assignedAt: string;\r\n assignedBy: string;\r\n /** The source group from which this role is inherited */\r\n sourceGroupId: string;\r\n sourceGroupName: string;\r\n /** How many levels up in the hierarchy (1 = direct parent) */\r\n inheritanceLevel: number;\r\n}\r\n\r\nexport interface EntraGroupMemberDto {\r\n userId: string;\r\n fullName: string;\r\n email: string;\r\n entraObjectId: string | null;\r\n isActive: boolean;\r\n addedAt: string;\r\n /** True if the member is directly in this group, false if via a child group */\r\n isDirect: boolean;\r\n /** Source group ID if member comes from a child group (null if direct) */\r\n sourceGroupId: string | null;\r\n /** Source group name if member comes from a child group */\r\n sourceGroupName: string | null;\r\n}\r\n\r\nexport interface AssignGroupToRoleRequest {\r\n roleId: string;\r\n /** If true, this role will be inherited by child groups. Default: true */\r\n isInheritable?: boolean;\r\n}\r\n\r\n// Types for Connection Test\r\nexport interface TestConnectionRequest {\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface TestConnectionResult {\r\n success: boolean;\r\n errorMessage: string | null;\r\n tenantName: string | null;\r\n userCount: number | null;\r\n groupCount: number | null;\r\n}\r\n\r\n// Types for Configuration\r\nexport interface EntraConfigurationDto {\r\n tenantId: string | null;\r\n clientId: string | null;\r\n hasClientSecret: boolean;\r\n syncEnabled: boolean;\r\n syncIntervalMinutes: number;\r\n useDeltaSync: boolean;\r\n defaultConflictResolution: string;\r\n autoCreateReferences: boolean;\r\n isConfigured: boolean;\r\n // User sync filters\r\n syncOnlyActiveUsers: boolean;\r\n /** Comma-separated list of user types to sync: member, guest, shared, room, equipment */\r\n syncUserTypes: string;\r\n}\r\n\r\nexport interface SaveEntraCredentialsRequest {\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface SaveEntraSyncSettingsRequest {\r\n syncEnabled: boolean;\r\n syncIntervalMinutes: number;\r\n useDeltaSync: boolean;\r\n defaultConflictResolution: string;\r\n autoCreateReferences: boolean;\r\n // User sync filters\r\n syncOnlyActiveUsers?: boolean;\r\n /** Comma-separated list of user types to sync: member, guest, shared, room, equipment */\r\n syncUserTypes?: string;\r\n}\r\n\r\n/**\r\n * Creates the Entra 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 createEntraApi = (tenantSlug?: string) => {\r\n const apiContext = tenantSlug ? withTenantContext(tenantSlug) : api;\r\n\r\n return {\r\n // Settings / Configuration\r\n settings: {\r\n /**\r\n * Get current Entra configuration\r\n */\r\n getConfiguration: () =>\r\n apiContext.get<EntraConfigurationDto>('/api/administration/entra/configuration'),\r\n\r\n /**\r\n * Test connection to Microsoft Entra ID with provided credentials\r\n */\r\n testConnection: (request: TestConnectionRequest) =>\r\n apiContext.post<TestConnectionResult>('/api/administration/entra/test-connection', request),\r\n\r\n /**\r\n * Test connection using stored credentials from database\r\n */\r\n testStoredConnection: () =>\r\n apiContext.post<TestConnectionResult>('/api/administration/entra/test-stored-connection'),\r\n\r\n /**\r\n * Save Entra credentials (TenantId, ClientId, ClientSecret)\r\n */\r\n saveCredentials: (request: SaveEntraCredentialsRequest) =>\r\n apiContext.put<{ message: string }>('/api/administration/entra/configuration/credentials', request),\r\n\r\n /**\r\n * Save Entra sync settings\r\n */\r\n saveSyncSettings: (request: SaveEntraSyncSettingsRequest) =>\r\n apiContext.put<{ message: string }>('/api/administration/entra/configuration/sync-settings', request),\r\n },\r\n\r\n // Dashboard / Sync Status\r\n dashboard: {\r\n /**\r\n * Get sync status for all resource types (Users, Groups, Memberships)\r\n */\r\n getSyncStatus: () =>\r\n apiContext.get<EntraSyncStatusDto[]>('/api/administration/entra/sync/status'),\r\n },\r\n\r\n // Sync Operations\r\n sync: {\r\n /**\r\n * Trigger full synchronization of all resources\r\n */\r\n syncAll: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync', options),\r\n\r\n /**\r\n * Trigger users synchronization only\r\n */\r\n syncUsers: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/users', options),\r\n\r\n /**\r\n * Trigger groups synchronization only\r\n */\r\n syncGroups: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/groups', options),\r\n\r\n /**\r\n * Trigger memberships synchronization only\r\n */\r\n syncMemberships: () =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/memberships'),\r\n\r\n /**\r\n * Force full sync (ignores delta tokens, re-syncs everything)\r\n */\r\n forceFullSync: () =>\r\n apiContext.post<{ message: string }>('/api/administration/entra/sync/force-full'),\r\n\r\n /**\r\n * Get sync history with pagination\r\n */\r\n getHistory: (page: number = 1, pageSize: number = 10) =>\r\n apiContext.get<EntraSyncHistoryResponse>(`/api/administration/entra/sync/history?page=${page}&pageSize=${pageSize}`),\r\n\r\n /**\r\n * Cancel a running sync session\r\n */\r\n cancelSession: (sessionId: string) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/sync/session/${sessionId}/cancel`),\r\n },\r\n\r\n // Groups Management\r\n groups: {\r\n /**\r\n * Get all Entra groups\r\n */\r\n getAll: () =>\r\n apiContext.get<EntraGroupDto[]>('/api/administration/entra/groups'),\r\n\r\n /**\r\n * Get a specific Entra group by ID\r\n */\r\n getById: (groupId: string) =>\r\n apiContext.get<EntraGroupDto>(`/api/administration/entra/groups/${groupId}`),\r\n\r\n /**\r\n * Assign a role to an Entra group\r\n */\r\n assignRole: (groupId: string, request: AssignGroupToRoleRequest) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/groups/${groupId}/roles`, request),\r\n\r\n /**\r\n * Remove a role from an Entra group\r\n */\r\n removeRole: (groupId: string, roleId: string) =>\r\n apiContext.delete<{ message: string }>(`/api/administration/entra/groups/${groupId}/roles/${roleId}`),\r\n\r\n /**\r\n * Get members of an Entra group\r\n */\r\n getMembers: (groupId: string) =>\r\n apiContext.get<EntraGroupMemberDto[]>(`/api/administration/entra/groups/${groupId}/members`),\r\n },\r\n\r\n // Users Management (Entra-related)\r\n users: {\r\n /**\r\n * Get Entra groups for a specific user\r\n */\r\n getGroups: (userId: string) =>\r\n apiContext.get<EntraGroupDto[]>(`/api/administration/entra/users/${userId}/groups`),\r\n },\r\n\r\n // Conflicts Management\r\n conflicts: {\r\n /**\r\n * Get all pending sync conflicts\r\n */\r\n getAll: () =>\r\n apiContext.get<EntraSyncConflictDto[]>('/api/administration/entra/conflicts'),\r\n\r\n /**\r\n * Resolve a specific conflict\r\n */\r\n resolve: (conflictId: string, data: ResolveConflictRequest) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/conflicts/${conflictId}/resolve`, data),\r\n },\r\n };\r\n};\r\n\r\n// Default export uses localStorage tenant slug (backward compatible)\r\nexport const entraApi = createEntraApi();\r\n\r\n// Export factory for explicit tenant context\r\nexport { createEntraApi };\r\n","import type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Cloud, Users, AlertCircle } from 'lucide-react';\r\nimport type { ConflictDataDto } from '@/services/api/entraApi';\r\nimport { formatPhoneNumber } from '@/utils/formatPhone';\r\n\r\ninterface ConflictDiffViewProps {\r\n readonly entraData: ConflictDataDto | null;\r\n readonly localData: ConflictDataDto | null;\r\n}\r\n\r\ninterface FieldDiff {\r\n key: string;\r\n label: string;\r\n entraValue: string | null;\r\n localValue: string | null;\r\n isDifferent: boolean;\r\n}\r\n\r\nconst normalizeValue = (value: string | null | undefined): string | null => {\r\n if (value === null || value === undefined || value === '') return null;\r\n return value.trim();\r\n};\r\n\r\nconst formatValue = (value: string | null, isPhone: boolean = false): string => {\r\n if (!value) return '-';\r\n if (isPhone) {\r\n const formatted = formatPhoneNumber(value, 'CH', 'international');\r\n return formatted || value;\r\n }\r\n return value;\r\n};\r\n\r\nexport function ConflictDiffView({ entraData, localData }: ConflictDiffViewProps): ReactElement | null {\r\n const { t } = useTranslation(['entra', 'common']);\r\n\r\n if (!entraData && !localData) {\r\n return null;\r\n }\r\n\r\n const fieldLabels: Record<string, string> = {\r\n firstName: t('entra:conflicts.fields.firstName', 'First Name'),\r\n lastName: t('entra:conflicts.fields.lastName', 'Last Name'),\r\n displayName: t('entra:conflicts.fields.displayName', 'Display Name'),\r\n email: t('entra:conflicts.fields.upn', 'UPN'),\r\n phoneNumber: t('entra:conflicts.fields.phoneNumber', 'Phone'),\r\n mobilePhone: t('entra:conflicts.fields.mobilePhone', 'Mobile'),\r\n department: t('entra:conflicts.fields.department', 'Department'),\r\n jobTitle: t('entra:conflicts.fields.jobTitle', 'Job Title'),\r\n };\r\n\r\n const phoneFields = ['phoneNumber', 'mobilePhone'];\r\n\r\n const fields: FieldDiff[] = Object.keys(fieldLabels).map((key) => {\r\n const entraValue = normalizeValue(entraData?.[key as keyof ConflictDataDto]);\r\n const localValue = normalizeValue(localData?.[key as keyof ConflictDataDto]);\r\n const isDifferent = entraValue !== localValue && (entraValue !== null || localValue !== null);\r\n\r\n return {\r\n key,\r\n label: fieldLabels[key],\r\n entraValue,\r\n localValue,\r\n isDifferent,\r\n };\r\n });\r\n\r\n const hasDifferences = fields.some((f) => f.isDifferent);\r\n\r\n if (!hasDifferences) {\r\n return (\r\n <div className=\"mt-4 pt-4 border-t border-[var(--border-color)]\">\r\n <p className=\"text-sm text-[var(--text-secondary)] italic\">\r\n {t('entra:conflicts.noDifferences', 'No field differences detected')}\r\n </p>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"mt-4 pt-4 border-t border-[var(--border-color)]\">\r\n <div className=\"overflow-hidden rounded-[var(--radius-card)] border border-[var(--border-color)]\">\r\n <table className=\"w-full text-sm\">\r\n <thead>\r\n <tr className=\"bg-[var(--bg-secondary)]\">\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--text-secondary)] w-1/4\">\r\n {t('entra:conflicts.field', 'Field')}\r\n </th>\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--color-accent-600)] w-[37.5%]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Cloud className=\"w-4 h-4\" />\r\n {t('entra:conflicts.entraData', 'Entra')}\r\n </div>\r\n </th>\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--success-text)] w-[37.5%]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Users className=\"w-4 h-4\" />\r\n {t('entra:conflicts.localData', 'Local')}\r\n </div>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody className=\"divide-y divide-[var(--border-color)]\">\r\n {fields\r\n .filter((f) => f.isDifferent)\r\n .map((field) => (\r\n <tr\r\n key={field.key}\r\n className=\"bg-[var(--bg-card)] hover:bg-[var(--bg-secondary)] transition-colors\"\r\n >\r\n <td className=\"px-4 py-3 font-medium text-[var(--text-primary)]\">\r\n <div className=\"flex items-center gap-2\">\r\n <AlertCircle className=\"w-4 h-4 text-[var(--color-accent-500)]\" />\r\n {field.label}\r\n </div>\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n {field.entraValue ? (\r\n <span className=\"text-[var(--color-accent-700)] bg-[var(--color-accent-500)]/10 px-2 py-1 rounded\">\r\n {formatValue(field.entraValue, phoneFields.includes(field.key))}\r\n </span>\r\n ) : (\r\n <span className=\"text-[var(--text-tertiary)] italic\">\r\n {formatValue(field.entraValue, phoneFields.includes(field.key))}\r\n </span>\r\n )}\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n {field.localValue ? (\r\n <span className=\"text-[var(--success-text)] bg-[var(--success-bg)] px-2 py-1 rounded\">\r\n {formatValue(field.localValue, phoneFields.includes(field.key))}\r\n </span>\r\n ) : (\r\n <span className=\"text-[var(--text-tertiary)] italic\">\r\n {formatValue(field.localValue, phoneFields.includes(field.key))}\r\n </span>\r\n )}\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n <p className=\"mt-2 text-xs text-[var(--text-tertiary)]\">\r\n {t('entra:conflicts.diffCount', '{{count}} field(s) with differences', {\r\n count: fields.filter((f) => f.isDifferent).length,\r\n })}\r\n </p>\r\n </div>\r\n );\r\n}\r\n","import { useMemo } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\n\r\n/**\r\n * Hook for locale-aware formatting utilities.\r\n * Automatically uses the current i18n language for formatting.\r\n */\r\nexport function useLocale(): {\n locale: string;\n formatDate: (date: string | Date, options?: Intl.DateTimeFormatOptions) => string;\n formatNumber: (value: number, options?: Intl.NumberFormatOptions) => string;\n formatCurrency: (value: number, currency?: string) => string;\n} {\r\n const { i18n } = useTranslation();\r\n\r\n const locale = useMemo(() => {\r\n // Map i18n language codes to Intl locale codes\r\n const localeMap: Record<string, string> = {\r\n fr: 'fr-FR',\r\n en: 'en-US',\r\n de: 'de-DE',\r\n it: 'it-IT',\r\n };\r\n return localeMap[i18n.language] || 'en-US';\r\n }, [i18n.language]);\r\n\r\n const formatDate = useMemo(() => {\r\n return (date: string | Date, options?: Intl.DateTimeFormatOptions) => {\r\n const defaultOptions: Intl.DateTimeFormatOptions = {\r\n dateStyle: 'medium',\r\n timeStyle: 'short',\r\n };\r\n return new Intl.DateTimeFormat(locale, options || defaultOptions).format(\r\n typeof date === 'string' ? new Date(date) : date\r\n );\r\n };\r\n }, [locale]);\r\n\r\n const formatNumber = useMemo(() => {\r\n return (value: number, options?: Intl.NumberFormatOptions) => {\r\n return new Intl.NumberFormat(locale, options).format(value);\r\n };\r\n }, [locale]);\r\n\r\n const formatCurrency = useMemo(() => {\r\n return (value: number, currency = 'EUR') => {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n }).format(value);\r\n };\r\n }, [locale]);\r\n\r\n return {\r\n locale,\r\n formatDate,\r\n formatNumber,\r\n formatCurrency,\r\n };\r\n}\r\n","import type { ReactElement } from 'react';\nimport { useTranslation } from 'react-i18next';\r\nimport {\r\n AlertTriangle,\r\n CheckCircle,\r\n Users,\r\n Building2,\r\n Link2,\r\n Cloud,\r\n Calendar,\r\n Mail,\r\n} from 'lucide-react';\r\nimport type { EntraSyncConflictDto } from '@/services/api/entraApi';\r\nimport { ConflictDiffView } from '@/components/platform/administration/ConflictDiffView';\r\nimport { useLocale } from '@/hooks/useLocale';\r\n\r\nexport interface ConflictCardProps {\r\n readonly conflict: EntraSyncConflictDto;\r\n readonly onResolve: (conflict: EntraSyncConflictDto) => void;\r\n readonly className?: string;\r\n}\r\n\r\nconst getResourceIcon = (resourceType: string) => {\r\n switch (resourceType) {\r\n case 'User':\r\n return Users;\r\n case 'Group':\r\n return Building2;\r\n case 'Membership':\r\n return Link2;\r\n default:\r\n return Cloud;\r\n }\r\n};\r\n\r\nconst getConflictTypeConfig = (conflictType: string) => {\r\n switch (conflictType) {\r\n case 'Duplicate':\r\n return {\r\n bg: 'bg-[var(--error-bg)]',\r\n text: 'text-[var(--error-text)]',\r\n border: 'border-[var(--error-border)]',\r\n };\r\n default:\r\n return {\r\n bg: 'bg-[var(--color-accent-500)]/10',\r\n text: 'text-[var(--color-accent-700)]',\r\n border: 'border-[var(--color-accent-300)]',\r\n };\r\n }\r\n};\r\n\r\nexport function ConflictCard({ conflict, onResolve, className = '' }: ConflictCardProps): ReactElement {\r\n const { t } = useTranslation(['entra']);\r\n const { formatDate } = useLocale();\r\n\r\n const ResourceIcon = getResourceIcon(conflict.resourceType);\r\n const typeConfig = getConflictTypeConfig(conflict.conflictType);\r\n\r\n return (\r\n <div className={`h-full flex flex-col rounded-[var(--radius-card)] border border-[var(--border-color)] bg-[var(--bg-card)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`}>\r\n {/* Header */}\r\n <div className=\"px-4 py-3 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-500)]/20\">\r\n <ResourceIcon className=\"w-5 h-5 text-[var(--color-accent-600)]\" />\r\n </div>\r\n <div>\r\n <h3 className=\"font-semibold text-[var(--text-primary)]\">\r\n {conflict.entraDisplayName}\r\n </h3>\r\n {conflict.entraEmail && (\r\n <div className=\"flex items-center gap-1 text-sm text-[var(--text-secondary)]\">\r\n <Mail className=\"w-3.5 h-3.5\" />\r\n {conflict.entraEmail}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${typeConfig.bg} ${typeConfig.text} border ${typeConfig.border}`}>\r\n <AlertTriangle className=\"w-3.5 h-3.5\" />\r\n {t(`entra:conflicts.types.${conflict.conflictType}`)}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 flex flex-col p-4 space-y-4\">\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {conflict.conflictDescription}\r\n </p>\r\n\r\n <ConflictDiffView entraData={conflict.entraData} localData={conflict.localData} />\r\n\r\n {/* Footer */}\r\n <div className=\"mt-auto flex items-center justify-between pt-4 border-t border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)]\">\r\n <Calendar className=\"w-3.5 h-3.5\" />\r\n {t('entra:conflicts.createdAt', { date: formatDate(conflict.createdAt) })}\r\n </div>\r\n <button\r\n onClick={() => onResolve(conflict)}\r\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-[var(--radius-button)] bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] transition-colors shadow-sm\"\r\n >\r\n <CheckCircle className=\"w-4 h-4\" />\r\n {t('entra:conflicts.resolve')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useEffect, useCallback, useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport {\r\n AlertTriangle,\r\n X,\r\n Loader2,\r\n Cloud,\r\n Users,\r\n Merge,\r\n SkipForward,\r\n} from 'lucide-react';\r\nimport type { EntraSyncConflictDto, ResolveConflictRequest } from '@/services/api/entraApi';\r\n\r\nconst NOTES_MAX_LENGTH = 500;\r\n\r\nexport interface ResolutionModalProps {\r\n readonly conflict: EntraSyncConflictDto;\r\n readonly onClose: () => void;\r\n readonly onResolve: (resolution: ResolveConflictRequest['resolution'], notes?: string) => Promise<void>;\r\n readonly resolving?: boolean;\r\n}\r\n\r\nconst getButtonClasses = (variant: string) => {\r\n switch (variant) {\r\n case 'primary':\r\n return 'bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] border-transparent';\r\n case 'success':\r\n return 'bg-[var(--success-bg)] text-[var(--success-text)] hover:opacity-90 border-[var(--success-border)]';\r\n case 'secondary':\r\n return 'bg-[var(--bg-secondary)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] border-[var(--border-color)]';\r\n case 'ghost':\r\n return 'bg-transparent text-[var(--text-secondary)] hover:bg-[var(--bg-secondary)] border-[var(--border-color)]';\r\n default:\r\n return 'bg-[var(--bg-secondary)] text-[var(--text-primary)] border-[var(--border-color)]';\r\n }\r\n};\r\n\r\nexport function ResolutionModal({\r\n conflict,\r\n onClose,\r\n onResolve,\r\n resolving = false,\r\n}: ResolutionModalProps): ReactElement {\r\n const { t } = useTranslation(['entra']);\r\n const [notes, setNotes] = useState('');\r\n\r\n const resolutionOptions = [\r\n {\r\n key: 'EntraPriority' as const,\r\n icon: Cloud,\r\n label: t('entra:conflicts.resolutions.entraWins'),\r\n variant: 'primary' as const,\r\n },\r\n {\r\n key: 'LocalPriority' as const,\r\n icon: Users,\r\n label: t('entra:conflicts.resolutions.localWins'),\r\n variant: 'success' as const,\r\n },\r\n {\r\n key: 'MergeToExisting' as const,\r\n icon: Merge,\r\n label: t('entra:conflicts.resolutions.merge'),\r\n variant: 'secondary' as const,\r\n },\r\n {\r\n key: 'SkipImport' as const,\r\n icon: SkipForward,\r\n label: t('entra:conflicts.resolutions.skip'),\r\n variant: 'ghost' as const,\r\n },\r\n ];\r\n\r\n // Handle escape key to close modal\r\n const handleKeyDown = useCallback((e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && !resolving) {\r\n onClose();\r\n }\r\n }, [onClose, resolving]);\r\n\r\n useEffect(() => {\r\n document.addEventListener('keydown', handleKeyDown);\r\n // Lock body scroll\r\n document.body.style.overflow = 'hidden';\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown);\r\n document.body.style.overflow = '';\r\n };\r\n }, [handleKeyDown]);\r\n\r\n const handleResolve = async (resolution: ResolveConflictRequest['resolution']) => {\r\n await onResolve(resolution, notes.trim() || undefined);\r\n };\r\n\r\n const handleNotesChange = (value: string) => {\r\n // Limit notes length\r\n if (value.length <= NOTES_MAX_LENGTH) {\r\n setNotes(value);\r\n }\r\n };\r\n\r\n return (\r\n <div\r\n className=\"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm\"\r\n onClick={(e) => {\r\n if (e.target === e.currentTarget && !resolving) {\r\n onClose();\r\n }\r\n }}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Escape' && !resolving) {\r\n onClose();\r\n }\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label=\"Resolve conflict\"\r\n >\r\n <div className=\"w-full max-w-lg rounded-[var(--radius-card)] bg-[var(--bg-card)] shadow-2xl overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"px-6 py-4 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-500)]/20\">\r\n <AlertTriangle className=\"w-5 h-5 text-[var(--color-accent-600)]\" />\r\n </div>\r\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">\r\n {t('entra:conflicts.resolveTitle')}\r\n </h2>\r\n </div>\r\n <button\r\n onClick={onClose}\r\n disabled={resolving}\r\n className=\"p-2 rounded-lg text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-secondary)] transition-colors disabled:opacity-50\"\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=\"p-6 space-y-4\">\r\n {/* Conflict Info */}\r\n <div className=\"p-4 rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)]\">\r\n <p className=\"font-medium text-[var(--text-primary)]\">{conflict.entraDisplayName}</p>\r\n <p className=\"text-sm text-[var(--text-secondary)] mt-1\">{conflict.conflictDescription}</p>\r\n </div>\r\n\r\n {/* Notes */}\r\n <div>\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <label className=\"block text-sm font-medium text-[var(--text-primary)]\">\r\n {t('entra:conflicts.notes')}\r\n </label>\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n {notes.length}/{NOTES_MAX_LENGTH}\r\n </span>\r\n </div>\r\n <textarea\r\n value={notes}\r\n onChange={(e) => handleNotesChange(e.target.value)}\r\n placeholder={t('entra:conflicts.notesPlaceholder')}\r\n maxLength={NOTES_MAX_LENGTH}\r\n disabled={resolving}\r\n className=\"w-full px-3 py-2.5 rounded-[var(--radius-input)] border border-[var(--border-color)] bg-[var(--bg-secondary)] text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-500)] focus:border-transparent transition-shadow resize-none disabled:opacity-50\"\r\n rows={3}\r\n />\r\n </div>\r\n\r\n {/* Resolution Options */}\r\n <div className=\"space-y-2\">\r\n {resolutionOptions.map((option) => (\r\n <button\r\n key={option.key}\r\n onClick={() => handleResolve(option.key)}\r\n disabled={resolving}\r\n className={`w-full flex items-center gap-3 px-4 py-3 rounded-[var(--radius-button)] border font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed ${getButtonClasses(option.variant)}`}\r\n >\r\n {resolving ? (\r\n <Loader2 className=\"w-5 h-5 animate-spin\" />\r\n ) : (\r\n <option.icon className=\"w-5 h-5\" />\r\n )}\r\n <span>{option.label}</span>\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"names":["withTenantContext","tenantSlug","url","config","apiClient","res","data","createEntraApi","apiContext","api","request","options","page","pageSize","sessionId","groupId","roleId","userId","conflictId","entraApi","normalizeValue","value","formatValue","isPhone","formatPhoneNumber","ConflictDiffView","entraData","localData","t","useTranslation","fieldLabels","phoneFields","fields","key","entraValue","localValue","isDifferent","f","jsxs","jsx","Cloud","Users","field","AlertCircle","useLocale","i18n","locale","useMemo","formatDate","date","defaultOptions","formatNumber","formatCurrency","currency","getResourceIcon","resourceType","Building2","Link2","getConflictTypeConfig","conflictType","ConflictCard","conflict","onResolve","className","ResourceIcon","typeConfig","Mail","AlertTriangle","Calendar","CheckCircle","NOTES_MAX_LENGTH","getButtonClasses","variant","ResolutionModal","onClose","resolving","notes","setNotes","useState","resolutionOptions","Merge","SkipForward","handleKeyDown","useCallback","e","useEffect","handleResolve","resolution","handleNotesChange","X","option","Loader2"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ResolutionModal-CxjANAOP.js","sources":["../../src/services/api/entraApi.ts","../../src/components/platform/administration/ConflictDiffView.tsx","../../src/hooks/useLocale.ts","../../src/components/platform/administration/entra/ConflictCard.tsx","../../src/components/platform/administration/entra/ResolutionModal.tsx"],"sourcesContent":["import { api, apiClient } from './apiClient';\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) => ({\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 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\n// Types for Entra Dashboard/Sync Status\r\nexport interface EntraSyncStatusDto {\r\n resourceType: string;\r\n status: string;\r\n lastFullSyncAt: string | null;\r\n lastDeltaSyncAt: string | null;\r\n totalItemsSynced: number;\r\n pendingConflicts: number;\r\n lastError: string | null;\r\n}\r\n\r\nexport interface EntraSyncResultDto {\r\n syncSessionId: string;\r\n resourceType: string;\r\n success: boolean;\r\n errorMessage: string | null;\r\n startedAt: string;\r\n completedAt: string;\r\n totalProcessed: number;\r\n created: number;\r\n updated: number;\r\n deleted: number;\r\n skipped: number;\r\n conflicts: number;\r\n pendingConflicts: EntraSyncConflictDto[];\r\n}\r\n\r\nexport interface EntraSyncOptionsDto {\r\n useDeltaSync?: boolean;\r\n syncUsers?: boolean;\r\n syncGroups?: boolean;\r\n syncMemberships?: boolean;\r\n conflictPriority?: 'ManualReview' | 'EntraWins' | 'LocalWins';\r\n autoCreateReferences?: boolean;\r\n}\r\n\r\n// Types for Entra Sync History\r\nexport interface EntraSyncSessionDto {\r\n id: string;\r\n resourceType: string;\r\n isFullSync: boolean;\r\n status: string;\r\n startedAt: string;\r\n completedAt: string | null;\r\n errorMessage: string | null;\r\n totalProcessed: number;\r\n created: number;\r\n updated: number;\r\n deleted: number;\r\n skipped: number;\r\n conflicts: number;\r\n triggerType: string;\r\n triggeredByUserId: string | null;\r\n triggeredByUserName: string | null;\r\n duration: string | null;\r\n}\r\n\r\nexport interface EntraSyncHistoryResponse {\r\n sessions: EntraSyncSessionDto[];\r\n totalCount: number;\r\n page: number;\r\n pageSize: number;\r\n}\r\n\r\n// Types for Entra Conflicts\r\nexport interface ConflictDataDto {\r\n firstName: string | null;\r\n lastName: string | null;\r\n displayName: string | null;\r\n email: string | null;\r\n phoneNumber: string | null;\r\n mobilePhone: string | null;\r\n department: string | null;\r\n jobTitle: string | null;\r\n}\r\n\r\nexport interface EntraSyncConflictDto {\r\n id: string;\r\n resourceType: string;\r\n entraObjectId: string;\r\n entraDisplayName: string;\r\n entraEmail: string | null;\r\n existingLocalEntityId: string | null;\r\n existingLocalDisplayName: string | null;\r\n conflictType: string;\r\n conflictDescription: string;\r\n resolution: string;\r\n createdAt: string;\r\n entraData: ConflictDataDto | null;\r\n localData: ConflictDataDto | null;\r\n}\r\n\r\nexport interface ResolveConflictRequest {\r\n resolution: 'EntraPriority' | 'LocalPriority' | 'SkipImport' | 'MergeToExisting' | 'CreateNew';\r\n notes?: string;\r\n}\r\n\r\n// Types for Entra Groups\r\nexport interface EntraGroupDto {\r\n id: string;\r\n entraObjectId: string;\r\n displayName: string;\r\n description: string | null;\r\n mail: string | null;\r\n isSecurityGroup: boolean;\r\n isActive: boolean;\r\n lastSyncedAt: string;\r\n memberCount: number;\r\n assignedRoles: EntraGroupRoleDto[];\r\n /** Roles inherited from parent groups in the hierarchy */\r\n inheritedRoles: InheritedRoleDto[];\r\n // Parent-child relationship for nested groups\r\n parentGroupId: string | null;\r\n parentGroupName: string | null;\r\n childGroupCount: number;\r\n childGroups: EntraGroupSummaryDto[];\r\n}\r\n\r\nexport interface EntraGroupSummaryDto {\r\n id: string;\r\n entraObjectId: string;\r\n displayName: string;\r\n isSecurityGroup: boolean;\r\n memberCount: number;\r\n childGroupCount: number;\r\n}\r\n\r\nexport interface EntraGroupRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n assignedAt: string;\r\n assignedBy: string;\r\n /** If true, this role is inherited by child groups in the hierarchy */\r\n isInheritable: boolean;\r\n}\r\n\r\n/**\r\n * Represents a role inherited from a parent group in the hierarchy\r\n */\r\nexport interface InheritedRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n assignedAt: string;\r\n assignedBy: string;\r\n /** The source group from which this role is inherited */\r\n sourceGroupId: string;\r\n sourceGroupName: string;\r\n /** How many levels up in the hierarchy (1 = direct parent) */\r\n inheritanceLevel: number;\r\n}\r\n\r\nexport interface EntraGroupMemberDto {\r\n userId: string;\r\n fullName: string;\r\n email: string;\r\n entraObjectId: string | null;\r\n isActive: boolean;\r\n addedAt: string;\r\n /** True if the member is directly in this group, false if via a child group */\r\n isDirect: boolean;\r\n /** Source group ID if member comes from a child group (null if direct) */\r\n sourceGroupId: string | null;\r\n /** Source group name if member comes from a child group */\r\n sourceGroupName: string | null;\r\n}\r\n\r\nexport interface AssignGroupToRoleRequest {\r\n roleId: string;\r\n /** If true, this role will be inherited by child groups. Default: true */\r\n isInheritable?: boolean;\r\n}\r\n\r\n// Types for Connection Test\r\nexport interface TestConnectionRequest {\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface TestConnectionResult {\r\n success: boolean;\r\n errorMessage: string | null;\r\n tenantName: string | null;\r\n userCount: number | null;\r\n groupCount: number | null;\r\n}\r\n\r\n// Types for Configuration\r\nexport interface EntraConfigurationDto {\r\n tenantId: string | null;\r\n clientId: string | null;\r\n hasClientSecret: boolean;\r\n syncEnabled: boolean;\r\n syncIntervalMinutes: number;\r\n useDeltaSync: boolean;\r\n defaultConflictResolution: string;\r\n autoCreateReferences: boolean;\r\n isConfigured: boolean;\r\n // User sync filters\r\n syncOnlyActiveUsers: boolean;\r\n /** Comma-separated list of user types to sync: member, guest, shared, room, equipment */\r\n syncUserTypes: string;\r\n}\r\n\r\nexport interface SaveEntraCredentialsRequest {\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface SaveEntraSyncSettingsRequest {\r\n syncEnabled: boolean;\r\n syncIntervalMinutes: number;\r\n useDeltaSync: boolean;\r\n defaultConflictResolution: string;\r\n autoCreateReferences: boolean;\r\n // User sync filters\r\n syncOnlyActiveUsers?: boolean;\r\n /** Comma-separated list of user types to sync: member, guest, shared, room, equipment */\r\n syncUserTypes?: string;\r\n}\r\n\r\n/**\r\n * Creates the Entra 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 createEntraApi = (tenantSlug?: string) => {\r\n const apiContext = tenantSlug ? withTenantContext(tenantSlug) : api;\r\n\r\n return {\r\n // Settings / Configuration\r\n settings: {\r\n /**\r\n * Get current Entra configuration\r\n */\r\n getConfiguration: () =>\r\n apiContext.get<EntraConfigurationDto>('/api/administration/entra/configuration'),\r\n\r\n /**\r\n * Test connection to Microsoft Entra ID with provided credentials\r\n */\r\n testConnection: (request: TestConnectionRequest) =>\r\n apiContext.post<TestConnectionResult>('/api/administration/entra/test-connection', request),\r\n\r\n /**\r\n * Test connection using stored credentials from database\r\n */\r\n testStoredConnection: () =>\r\n apiContext.post<TestConnectionResult>('/api/administration/entra/test-stored-connection'),\r\n\r\n /**\r\n * Save Entra credentials (TenantId, ClientId, ClientSecret)\r\n */\r\n saveCredentials: (request: SaveEntraCredentialsRequest) =>\r\n apiContext.put<{ message: string }>('/api/administration/entra/configuration/credentials', request),\r\n\r\n /**\r\n * Save Entra sync settings\r\n */\r\n saveSyncSettings: (request: SaveEntraSyncSettingsRequest) =>\r\n apiContext.put<{ message: string }>('/api/administration/entra/configuration/sync-settings', request),\r\n },\r\n\r\n // Dashboard / Sync Status\r\n dashboard: {\r\n /**\r\n * Get sync status for all resource types (Users, Groups, Memberships)\r\n */\r\n getSyncStatus: () =>\r\n apiContext.get<EntraSyncStatusDto[]>('/api/administration/entra/sync/status'),\r\n },\r\n\r\n // Sync Operations\r\n sync: {\r\n /**\r\n * Trigger full synchronization of all resources\r\n */\r\n syncAll: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync', options),\r\n\r\n /**\r\n * Trigger users synchronization only\r\n */\r\n syncUsers: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/users', options),\r\n\r\n /**\r\n * Trigger groups synchronization only\r\n */\r\n syncGroups: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/groups', options),\r\n\r\n /**\r\n * Trigger memberships synchronization only\r\n */\r\n syncMemberships: () =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/memberships'),\r\n\r\n /**\r\n * Force full sync (ignores delta tokens, re-syncs everything)\r\n */\r\n forceFullSync: () =>\r\n apiContext.post<{ message: string }>('/api/administration/entra/sync/force-full'),\r\n\r\n /**\r\n * Get sync history with pagination\r\n */\r\n getHistory: (page: number = 1, pageSize: number = 10) =>\r\n apiContext.get<EntraSyncHistoryResponse>(`/api/administration/entra/sync/history?page=${page}&pageSize=${pageSize}`),\r\n\r\n /**\r\n * Cancel a running sync session\r\n */\r\n cancelSession: (sessionId: string) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/sync/session/${sessionId}/cancel`),\r\n },\r\n\r\n // Groups Management\r\n groups: {\r\n /**\r\n * Get all Entra groups\r\n */\r\n getAll: () =>\r\n apiContext.get<EntraGroupDto[]>('/api/administration/entra/groups'),\r\n\r\n /**\r\n * Get a specific Entra group by ID\r\n */\r\n getById: (groupId: string) =>\r\n apiContext.get<EntraGroupDto>(`/api/administration/entra/groups/${groupId}`),\r\n\r\n /**\r\n * Assign a role to an Entra group\r\n */\r\n assignRole: (groupId: string, request: AssignGroupToRoleRequest) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/groups/${groupId}/roles`, request),\r\n\r\n /**\r\n * Remove a role from an Entra group\r\n */\r\n removeRole: (groupId: string, roleId: string) =>\r\n apiContext.delete<{ message: string }>(`/api/administration/entra/groups/${groupId}/roles/${roleId}`),\r\n\r\n /**\r\n * Get members of an Entra group\r\n */\r\n getMembers: (groupId: string) =>\r\n apiContext.get<EntraGroupMemberDto[]>(`/api/administration/entra/groups/${groupId}/members`),\r\n },\r\n\r\n // Users Management (Entra-related)\r\n users: {\r\n /**\r\n * Get Entra groups for a specific user\r\n */\r\n getGroups: (userId: string) =>\r\n apiContext.get<EntraGroupDto[]>(`/api/administration/entra/users/${userId}/groups`),\r\n },\r\n\r\n // Conflicts Management\r\n conflicts: {\r\n /**\r\n * Get all pending sync conflicts\r\n */\r\n getAll: () =>\r\n apiContext.get<EntraSyncConflictDto[]>('/api/administration/entra/conflicts'),\r\n\r\n /**\r\n * Resolve a specific conflict\r\n */\r\n resolve: (conflictId: string, data: ResolveConflictRequest) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/conflicts/${conflictId}/resolve`, data),\r\n },\r\n };\r\n};\r\n\r\n// Default export uses localStorage tenant slug (backward compatible)\r\nexport const entraApi = createEntraApi();\r\n\r\n// Export factory for explicit tenant context\r\nexport { createEntraApi };\r\n","import type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Cloud, Users, AlertCircle } from 'lucide-react';\r\nimport type { ConflictDataDto } from '@/services/api/entraApi';\r\nimport { formatPhoneNumber } from '@/utils/formatPhone';\r\n\r\ninterface ConflictDiffViewProps {\r\n readonly entraData: ConflictDataDto | null;\r\n readonly localData: ConflictDataDto | null;\r\n}\r\n\r\ninterface FieldDiff {\r\n key: string;\r\n label: string;\r\n entraValue: string | null;\r\n localValue: string | null;\r\n isDifferent: boolean;\r\n}\r\n\r\nconst normalizeValue = (value: string | null | undefined): string | null => {\r\n if (value === null || value === undefined || value === '') return null;\r\n return value.trim();\r\n};\r\n\r\nconst formatValue = (value: string | null, isPhone: boolean = false): string => {\r\n if (!value) return '-';\r\n if (isPhone) {\r\n const formatted = formatPhoneNumber(value, 'CH', 'international');\r\n return formatted || value;\r\n }\r\n return value;\r\n};\r\n\r\nexport function ConflictDiffView({ entraData, localData }: ConflictDiffViewProps): ReactElement | null {\r\n const { t } = useTranslation(['entra', 'common']);\r\n\r\n if (!entraData && !localData) {\r\n return null;\r\n }\r\n\r\n const fieldLabels: Record<string, string> = {\r\n firstName: t('entra:conflicts.fields.firstName', 'First Name'),\r\n lastName: t('entra:conflicts.fields.lastName', 'Last Name'),\r\n displayName: t('entra:conflicts.fields.displayName', 'Display Name'),\r\n email: t('entra:conflicts.fields.upn', 'UPN'),\r\n phoneNumber: t('entra:conflicts.fields.phoneNumber', 'Phone'),\r\n mobilePhone: t('entra:conflicts.fields.mobilePhone', 'Mobile'),\r\n department: t('entra:conflicts.fields.department', 'Department'),\r\n jobTitle: t('entra:conflicts.fields.jobTitle', 'Job Title'),\r\n };\r\n\r\n const phoneFields = ['phoneNumber', 'mobilePhone'];\r\n\r\n const fields: FieldDiff[] = Object.keys(fieldLabels).map((key) => {\r\n const entraValue = normalizeValue(entraData?.[key as keyof ConflictDataDto]);\r\n const localValue = normalizeValue(localData?.[key as keyof ConflictDataDto]);\r\n const isDifferent = entraValue !== localValue && (entraValue !== null || localValue !== null);\r\n\r\n return {\r\n key,\r\n label: fieldLabels[key],\r\n entraValue,\r\n localValue,\r\n isDifferent,\r\n };\r\n });\r\n\r\n const hasDifferences = fields.some((f) => f.isDifferent);\r\n\r\n if (!hasDifferences) {\r\n return (\r\n <div className=\"mt-4 pt-4 border-t border-[var(--border-color)]\">\r\n <p className=\"text-sm text-[var(--text-secondary)] italic\">\r\n {t('entra:conflicts.noDifferences', 'No field differences detected')}\r\n </p>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"mt-4 pt-4 border-t border-[var(--border-color)]\">\r\n <div className=\"overflow-hidden rounded-[var(--radius-card)] border border-[var(--border-color)]\">\r\n <table className=\"w-full text-sm\">\r\n <thead>\r\n <tr className=\"bg-[var(--bg-secondary)]\">\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--text-secondary)] w-1/4\">\r\n {t('entra:conflicts.field', 'Field')}\r\n </th>\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--color-accent-600)] w-[37.5%]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Cloud className=\"w-4 h-4\" />\r\n {t('entra:conflicts.entraData', 'Entra')}\r\n </div>\r\n </th>\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--success-text)] w-[37.5%]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Users className=\"w-4 h-4\" />\r\n {t('entra:conflicts.localData', 'Local')}\r\n </div>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody className=\"divide-y divide-[var(--border-color)]\">\r\n {fields\r\n .filter((f) => f.isDifferent)\r\n .map((field) => (\r\n <tr\r\n key={field.key}\r\n className=\"bg-[var(--bg-card)] hover:bg-[var(--bg-secondary)] transition-colors\"\r\n >\r\n <td className=\"px-4 py-3 font-medium text-[var(--text-primary)]\">\r\n <div className=\"flex items-center gap-2\">\r\n <AlertCircle className=\"w-4 h-4 text-[var(--color-accent-500)]\" />\r\n {field.label}\r\n </div>\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n {field.entraValue ? (\r\n <span className=\"text-[var(--color-accent-700)] bg-[var(--color-accent-500)]/10 px-2 py-1 rounded\">\r\n {formatValue(field.entraValue, phoneFields.includes(field.key))}\r\n </span>\r\n ) : (\r\n <span className=\"text-[var(--text-tertiary)] italic\">\r\n {formatValue(field.entraValue, phoneFields.includes(field.key))}\r\n </span>\r\n )}\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n {field.localValue ? (\r\n <span className=\"text-[var(--success-text)] bg-[var(--success-bg)] px-2 py-1 rounded\">\r\n {formatValue(field.localValue, phoneFields.includes(field.key))}\r\n </span>\r\n ) : (\r\n <span className=\"text-[var(--text-tertiary)] italic\">\r\n {formatValue(field.localValue, phoneFields.includes(field.key))}\r\n </span>\r\n )}\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n <p className=\"mt-2 text-xs text-[var(--text-tertiary)]\">\r\n {t('entra:conflicts.diffCount', '{{count}} field(s) with differences', {\r\n count: fields.filter((f) => f.isDifferent).length,\r\n })}\r\n </p>\r\n </div>\r\n );\r\n}\r\n","import { useMemo } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\n\r\n/**\r\n * Hook for locale-aware formatting utilities.\r\n * Automatically uses the current i18n language for formatting.\r\n */\r\nexport function useLocale(): {\n locale: string;\n formatDate: (date: string | Date, options?: Intl.DateTimeFormatOptions) => string;\n formatNumber: (value: number, options?: Intl.NumberFormatOptions) => string;\n formatCurrency: (value: number, currency?: string) => string;\n} {\r\n const { i18n } = useTranslation();\r\n\r\n const locale = useMemo(() => {\r\n // Map i18n language codes to Intl locale codes\r\n const localeMap: Record<string, string> = {\r\n fr: 'fr-FR',\r\n en: 'en-US',\r\n de: 'de-DE',\r\n it: 'it-IT',\r\n };\r\n return localeMap[i18n.language] || 'en-US';\r\n }, [i18n.language]);\r\n\r\n const formatDate = useMemo(() => {\r\n return (date: string | Date, options?: Intl.DateTimeFormatOptions) => {\r\n const defaultOptions: Intl.DateTimeFormatOptions = {\r\n dateStyle: 'medium',\r\n timeStyle: 'short',\r\n };\r\n return new Intl.DateTimeFormat(locale, options || defaultOptions).format(\r\n typeof date === 'string' ? new Date(date) : date\r\n );\r\n };\r\n }, [locale]);\r\n\r\n const formatNumber = useMemo(() => {\r\n return (value: number, options?: Intl.NumberFormatOptions) => {\r\n return new Intl.NumberFormat(locale, options).format(value);\r\n };\r\n }, [locale]);\r\n\r\n const formatCurrency = useMemo(() => {\r\n return (value: number, currency = 'EUR') => {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n }).format(value);\r\n };\r\n }, [locale]);\r\n\r\n return {\r\n locale,\r\n formatDate,\r\n formatNumber,\r\n formatCurrency,\r\n };\r\n}\r\n","import type { ReactElement } from 'react';\nimport { useTranslation } from 'react-i18next';\r\nimport {\r\n AlertTriangle,\r\n CheckCircle,\r\n Users,\r\n Building2,\r\n Link2,\r\n Cloud,\r\n Calendar,\r\n Mail,\r\n} from 'lucide-react';\r\nimport type { EntraSyncConflictDto } from '@/services/api/entraApi';\r\nimport { ConflictDiffView } from '@/components/platform/administration/ConflictDiffView';\r\nimport { useLocale } from '@/hooks/useLocale';\r\n\r\nexport interface ConflictCardProps {\r\n readonly conflict: EntraSyncConflictDto;\r\n readonly onResolve: (conflict: EntraSyncConflictDto) => void;\r\n readonly className?: string;\r\n}\r\n\r\nconst getResourceIcon = (resourceType: string) => {\r\n switch (resourceType) {\r\n case 'User':\r\n return Users;\r\n case 'Group':\r\n return Building2;\r\n case 'Membership':\r\n return Link2;\r\n default:\r\n return Cloud;\r\n }\r\n};\r\n\r\nconst getConflictTypeConfig = (conflictType: string) => {\r\n switch (conflictType) {\r\n case 'Duplicate':\r\n return {\r\n bg: 'bg-[var(--error-bg)]',\r\n text: 'text-[var(--error-text)]',\r\n border: 'border-[var(--error-border)]',\r\n };\r\n default:\r\n return {\r\n bg: 'bg-[var(--color-accent-500)]/10',\r\n text: 'text-[var(--color-accent-700)]',\r\n border: 'border-[var(--color-accent-300)]',\r\n };\r\n }\r\n};\r\n\r\nexport function ConflictCard({ conflict, onResolve, className = '' }: ConflictCardProps): ReactElement {\r\n const { t } = useTranslation(['entra']);\r\n const { formatDate } = useLocale();\r\n\r\n const ResourceIcon = getResourceIcon(conflict.resourceType);\r\n const typeConfig = getConflictTypeConfig(conflict.conflictType);\r\n\r\n return (\r\n <div className={`h-full flex flex-col rounded-[var(--radius-card)] border border-[var(--border-color)] bg-[var(--bg-card)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`}>\r\n {/* Header */}\r\n <div className=\"px-4 py-3 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-500)]/20\">\r\n <ResourceIcon className=\"w-5 h-5 text-[var(--color-accent-600)]\" />\r\n </div>\r\n <div>\r\n <h3 className=\"font-semibold text-[var(--text-primary)]\">\r\n {conflict.entraDisplayName}\r\n </h3>\r\n {conflict.entraEmail && (\r\n <div className=\"flex items-center gap-1 text-sm text-[var(--text-secondary)]\">\r\n <Mail className=\"w-3.5 h-3.5\" />\r\n {conflict.entraEmail}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${typeConfig.bg} ${typeConfig.text} border ${typeConfig.border}`}>\r\n <AlertTriangle className=\"w-3.5 h-3.5\" />\r\n {t(`entra:conflicts.types.${conflict.conflictType}`)}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 flex flex-col p-4 space-y-4\">\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {conflict.conflictDescription}\r\n </p>\r\n\r\n <ConflictDiffView entraData={conflict.entraData} localData={conflict.localData} />\r\n\r\n {/* Footer */}\r\n <div className=\"mt-auto flex items-center justify-between pt-4 border-t border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)]\">\r\n <Calendar className=\"w-3.5 h-3.5\" />\r\n {t('entra:conflicts.createdAt', { date: formatDate(conflict.createdAt) })}\r\n </div>\r\n <button\r\n onClick={() => onResolve(conflict)}\r\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-[var(--radius-button)] bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] transition-colors shadow-sm\"\r\n >\r\n <CheckCircle className=\"w-4 h-4\" />\r\n {t('entra:conflicts.resolve')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useEffect, useCallback, useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport {\r\n AlertTriangle,\r\n X,\r\n Loader2,\r\n Cloud,\r\n Users,\r\n Merge,\r\n SkipForward,\r\n} from 'lucide-react';\r\nimport type { EntraSyncConflictDto, ResolveConflictRequest } from '@/services/api/entraApi';\r\n\r\nconst NOTES_MAX_LENGTH = 500;\r\n\r\nexport interface ResolutionModalProps {\r\n readonly conflict: EntraSyncConflictDto;\r\n readonly onClose: () => void;\r\n readonly onResolve: (resolution: ResolveConflictRequest['resolution'], notes?: string) => Promise<void>;\r\n readonly resolving?: boolean;\r\n}\r\n\r\nconst getButtonClasses = (variant: string) => {\r\n switch (variant) {\r\n case 'primary':\r\n return 'bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] border-transparent';\r\n case 'success':\r\n return 'bg-[var(--success-bg)] text-[var(--success-text)] hover:opacity-90 border-[var(--success-border)]';\r\n case 'secondary':\r\n return 'bg-[var(--bg-secondary)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] border-[var(--border-color)]';\r\n case 'ghost':\r\n return 'bg-transparent text-[var(--text-secondary)] hover:bg-[var(--bg-secondary)] border-[var(--border-color)]';\r\n default:\r\n return 'bg-[var(--bg-secondary)] text-[var(--text-primary)] border-[var(--border-color)]';\r\n }\r\n};\r\n\r\nexport function ResolutionModal({\r\n conflict,\r\n onClose,\r\n onResolve,\r\n resolving = false,\r\n}: ResolutionModalProps): ReactElement {\r\n const { t } = useTranslation(['entra']);\r\n const [notes, setNotes] = useState('');\r\n\r\n const resolutionOptions = [\r\n {\r\n key: 'EntraPriority' as const,\r\n icon: Cloud,\r\n label: t('entra:conflicts.resolutions.entraWins'),\r\n variant: 'primary' as const,\r\n },\r\n {\r\n key: 'LocalPriority' as const,\r\n icon: Users,\r\n label: t('entra:conflicts.resolutions.localWins'),\r\n variant: 'success' as const,\r\n },\r\n {\r\n key: 'MergeToExisting' as const,\r\n icon: Merge,\r\n label: t('entra:conflicts.resolutions.merge'),\r\n variant: 'secondary' as const,\r\n },\r\n {\r\n key: 'SkipImport' as const,\r\n icon: SkipForward,\r\n label: t('entra:conflicts.resolutions.skip'),\r\n variant: 'ghost' as const,\r\n },\r\n ];\r\n\r\n // Handle escape key to close modal\r\n const handleKeyDown = useCallback((e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && !resolving) {\r\n onClose();\r\n }\r\n }, [onClose, resolving]);\r\n\r\n useEffect(() => {\r\n document.addEventListener('keydown', handleKeyDown);\r\n // Lock body scroll\r\n document.body.style.overflow = 'hidden';\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown);\r\n document.body.style.overflow = '';\r\n };\r\n }, [handleKeyDown]);\r\n\r\n const handleResolve = async (resolution: ResolveConflictRequest['resolution']) => {\r\n await onResolve(resolution, notes.trim() || undefined);\r\n };\r\n\r\n const handleNotesChange = (value: string) => {\r\n // Limit notes length\r\n if (value.length <= NOTES_MAX_LENGTH) {\r\n setNotes(value);\r\n }\r\n };\r\n\r\n return (\r\n <div\r\n className=\"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm\"\r\n onClick={(e) => {\r\n if (e.target === e.currentTarget && !resolving) {\r\n onClose();\r\n }\r\n }}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Escape' && !resolving) {\r\n onClose();\r\n }\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label=\"Resolve conflict\"\r\n >\r\n <div className=\"w-full max-w-lg rounded-[var(--radius-card)] bg-[var(--bg-card)] shadow-2xl overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"px-6 py-4 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-500)]/20\">\r\n <AlertTriangle className=\"w-5 h-5 text-[var(--color-accent-600)]\" />\r\n </div>\r\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">\r\n {t('entra:conflicts.resolveTitle')}\r\n </h2>\r\n </div>\r\n <button\r\n onClick={onClose}\r\n disabled={resolving}\r\n className=\"p-2 rounded-lg text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-secondary)] transition-colors disabled:opacity-50\"\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=\"p-6 space-y-4\">\r\n {/* Conflict Info */}\r\n <div className=\"p-4 rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)]\">\r\n <p className=\"font-medium text-[var(--text-primary)]\">{conflict.entraDisplayName}</p>\r\n <p className=\"text-sm text-[var(--text-secondary)] mt-1\">{conflict.conflictDescription}</p>\r\n </div>\r\n\r\n {/* Notes */}\r\n <div>\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <label className=\"block text-sm font-medium text-[var(--text-primary)]\">\r\n {t('entra:conflicts.notes')}\r\n </label>\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n {notes.length}/{NOTES_MAX_LENGTH}\r\n </span>\r\n </div>\r\n <textarea\r\n value={notes}\r\n onChange={(e) => handleNotesChange(e.target.value)}\r\n placeholder={t('entra:conflicts.notesPlaceholder')}\r\n maxLength={NOTES_MAX_LENGTH}\r\n disabled={resolving}\r\n className=\"w-full px-3 py-2.5 rounded-[var(--radius-input)] border border-[var(--border-color)] bg-[var(--bg-secondary)] text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-500)] focus:border-transparent transition-shadow resize-none disabled:opacity-50\"\r\n rows={3}\r\n />\r\n </div>\r\n\r\n {/* Resolution Options */}\r\n <div className=\"space-y-2\">\r\n {resolutionOptions.map((option) => (\r\n <button\r\n key={option.key}\r\n onClick={() => handleResolve(option.key)}\r\n disabled={resolving}\r\n className={`w-full flex items-center gap-3 px-4 py-3 rounded-[var(--radius-button)] border font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed ${getButtonClasses(option.variant)}`}\r\n >\r\n {resolving ? (\r\n <Loader2 className=\"w-5 h-5 animate-spin\" />\r\n ) : (\r\n <option.icon className=\"w-5 h-5\" />\r\n )}\r\n <span>{option.label}</span>\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"names":["withTenantContext","tenantSlug","url","config","apiClient","res","data","createEntraApi","apiContext","api","request","options","page","pageSize","sessionId","groupId","roleId","userId","conflictId","entraApi","normalizeValue","value","formatValue","isPhone","formatPhoneNumber","ConflictDiffView","entraData","localData","t","useTranslation","fieldLabels","phoneFields","fields","key","entraValue","localValue","isDifferent","f","jsxs","jsx","Cloud","Users","field","AlertCircle","useLocale","i18n","locale","useMemo","formatDate","date","defaultOptions","formatNumber","formatCurrency","currency","getResourceIcon","resourceType","Building2","Link2","getConflictTypeConfig","conflictType","ConflictCard","conflict","onResolve","className","ResourceIcon","typeConfig","Mail","AlertTriangle","Calendar","CheckCircle","NOTES_MAX_LENGTH","getButtonClasses","variant","ResolutionModal","onClose","resolving","notes","setNotes","useState","resolutionOptions","Merge","SkipForward","handleKeyDown","useCallback","e","useEffect","handleResolve","resolution","handleNotesChange","X","option","Loader2"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const p=require("./index-2wUhd9Lu.js"),e=require("react/jsx-runtime"),h=require("react-i18next"),l=require("lucide-react"),N=require("./formatPhone-D4luHK-2.js"),u=require("react"),j=a=>({get:(r,t)=>p.apiClient.get(r,{...t,headers:a?{...t?.headers,"X-Tenant-Slug":a}:t?.headers}).then(s=>s.data),post:(r,t,s)=>p.apiClient.post(r,t,{...s,headers:a?{...s?.headers,"X-Tenant-Slug":a}:s?.headers}).then(o=>o.data),put:(r,t,s)=>p.apiClient.put(r,t,{...s,headers:a?{...s?.headers,"X-Tenant-Slug":a}:s?.headers}).then(o=>o.data),delete:(r,t)=>p.apiClient.delete(r,{...t,headers:a?{...t?.headers,"X-Tenant-Slug":a}:t?.headers}).then(s=>s.data)}),g=a=>{const r=a?j(a):p.api;return{settings:{getConfiguration:()=>r.get("/api/administration/entra/configuration"),testConnection:t=>r.post("/api/administration/entra/test-connection",t),testStoredConnection:()=>r.post("/api/administration/entra/test-stored-connection"),saveCredentials:t=>r.put("/api/administration/entra/configuration/credentials",t),saveSyncSettings:t=>r.put("/api/administration/entra/configuration/sync-settings",t)},dashboard:{getSyncStatus:()=>r.get("/api/administration/entra/sync/status")},sync:{syncAll:t=>r.post("/api/administration/entra/sync",t),syncUsers:t=>r.post("/api/administration/entra/sync/users",t),syncGroups:t=>r.post("/api/administration/entra/sync/groups",t),syncMemberships:()=>r.post("/api/administration/entra/sync/memberships"),forceFullSync:()=>r.post("/api/administration/entra/sync/force-full"),getHistory:(t=1,s=10)=>r.get(`/api/administration/entra/sync/history?page=${t}&pageSize=${s}`),cancelSession:t=>r.post(`/api/administration/entra/sync/session/${t}/cancel`)},groups:{getAll:()=>r.get("/api/administration/entra/groups"),getById:t=>r.get(`/api/administration/entra/groups/${t}`),assignRole:(t,s)=>r.post(`/api/administration/entra/groups/${t}/roles`,s),removeRole:(t,s)=>r.delete(`/api/administration/entra/groups/${t}/roles/${s}`),getMembers:t=>r.get(`/api/administration/entra/groups/${t}/members`)},users:{getGroups:t=>r.get(`/api/administration/entra/users/${t}/groups`)},conflicts:{getAll:()=>r.get("/api/administration/entra/conflicts"),resolve:(t,s)=>r.post(`/api/administration/entra/conflicts/${t}/resolve`,s)}}},w=g(),y=a=>a==null||a===""?null:a.trim(),b=(a,r=!1)=>a?r&&N.formatPhoneNumber(a,"CH","international")||a:"-";function C({entraData:a,localData:r}){const{t}=h.useTranslation(["entra","common"]);if(!a&&!r)return null;const s={firstName:t("entra:conflicts.fields.firstName","First Name"),lastName:t("entra:conflicts.fields.lastName","Last Name"),displayName:t("entra:conflicts.fields.displayName","Display Name"),email:t("entra:conflicts.fields.upn","UPN"),phoneNumber:t("entra:conflicts.fields.phoneNumber","Phone"),mobilePhone:t("entra:conflicts.fields.mobilePhone","Mobile"),department:t("entra:conflicts.fields.department","Department"),jobTitle:t("entra:conflicts.fields.jobTitle","Job Title")},o=["phoneNumber","mobilePhone"],i=Object.keys(s).map(n=>{const m=y(a?.[n]),x=y(r?.[n]),f=m!==x&&(m!==null||x!==null);return{key:n,label:s[n],entraValue:m,localValue:x,isDifferent:f}});return i.some(n=>n.isDifferent)?e.jsxs("div",{className:"mt-4 pt-4 border-t border-[var(--border-color)]",children:[e.jsx("div",{className:"overflow-hidden rounded-[var(--radius-card)] border border-[var(--border-color)]",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"bg-[var(--bg-secondary)]",children:[e.jsx("th",{className:"px-4 py-2 text-left font-medium text-[var(--text-secondary)] w-1/4",children:t("entra:conflicts.field","Field")}),e.jsx("th",{className:"px-4 py-2 text-left font-medium text-[var(--color-accent-600)] w-[37.5%]",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(l.Cloud,{className:"w-4 h-4"}),t("entra:conflicts.entraData","Entra")]})}),e.jsx("th",{className:"px-4 py-2 text-left font-medium text-[var(--success-text)] w-[37.5%]",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(l.Users,{className:"w-4 h-4"}),t("entra:conflicts.localData","Local")]})})]})}),e.jsx("tbody",{className:"divide-y divide-[var(--border-color)]",children:i.filter(n=>n.isDifferent).map(n=>e.jsxs("tr",{className:"bg-[var(--bg-card)] hover:bg-[var(--bg-secondary)] transition-colors",children:[e.jsx("td",{className:"px-4 py-3 font-medium text-[var(--text-primary)]",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(l.AlertCircle,{className:"w-4 h-4 text-[var(--color-accent-500)]"}),n.label]})}),e.jsx("td",{className:"px-4 py-3",children:n.entraValue?e.jsx("span",{className:"text-[var(--color-accent-700)] bg-[var(--color-accent-500)]/10 px-2 py-1 rounded",children:b(n.entraValue,o.includes(n.key))}):e.jsx("span",{className:"text-[var(--text-tertiary)] italic",children:b(n.entraValue,o.includes(n.key))})}),e.jsx("td",{className:"px-4 py-3",children:n.localValue?e.jsx("span",{className:"text-[var(--success-text)] bg-[var(--success-bg)] px-2 py-1 rounded",children:b(n.localValue,o.includes(n.key))}):e.jsx("span",{className:"text-[var(--text-tertiary)] italic",children:b(n.localValue,o.includes(n.key))})})]},n.key))})]})}),e.jsx("p",{className:"mt-2 text-xs text-[var(--text-tertiary)]",children:t("entra:conflicts.diffCount","{{count}} field(s) with differences",{count:i.filter(n=>n.isDifferent).length})})]}):e.jsx("div",{className:"mt-4 pt-4 border-t border-[var(--border-color)]",children:e.jsx("p",{className:"text-sm text-[var(--text-secondary)] italic",children:t("entra:conflicts.noDifferences","No field differences detected")})})}function k(){const{i18n:a}=h.useTranslation(),r=u.useMemo(()=>({fr:"fr-FR",en:"en-US",de:"de-DE",it:"it-IT"})[a.language]||"en-US",[a.language]),t=u.useMemo(()=>(i,d)=>{const n={dateStyle:"medium",timeStyle:"short"};return new Intl.DateTimeFormat(r,d||n).format(typeof i=="string"?new Date(i):i)},[r]),s=u.useMemo(()=>(i,d)=>new Intl.NumberFormat(r,d).format(i),[r]),o=u.useMemo(()=>(i,d="EUR")=>new Intl.NumberFormat(r,{style:"currency",currency:d}).format(i),[r]);return{locale:r,formatDate:t,formatNumber:s,formatCurrency:o}}const D=a=>{switch(a){case"User":return l.Users;case"Group":return l.Building2;case"Membership":return l.Link2;default:return l.Cloud}},T=a=>a==="Duplicate"?{bg:"bg-[var(--error-bg)]",text:"text-[var(--error-text)]",border:"border-[var(--error-border)]"}:{bg:"bg-[var(--color-accent-500)]/10",text:"text-[var(--color-accent-700)]",border:"border-[var(--color-accent-300)]"};function E({conflict:a,onResolve:r,className:t=""}){const{t:s}=h.useTranslation(["entra"]),{formatDate:o}=k(),i=D(a.resourceType),d=T(a.conflictType);return e.jsxs("div",{className:`h-full flex flex-col rounded-[var(--radius-card)] border border-[var(--border-color)] bg-[var(--bg-card)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${t}`,children:[e.jsx("div",{className:"px-4 py-3 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 rounded-lg bg-[var(--color-accent-500)]/20",children:e.jsx(i,{className:"w-5 h-5 text-[var(--color-accent-600)]"})}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-[var(--text-primary)]",children:a.entraDisplayName}),a.entraEmail&&e.jsxs("div",{className:"flex items-center gap-1 text-sm text-[var(--text-secondary)]",children:[e.jsx(l.Mail,{className:"w-3.5 h-3.5"}),a.entraEmail]})]})]}),e.jsxs("span",{className:`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${d.bg} ${d.text} border ${d.border}`,children:[e.jsx(l.AlertTriangle,{className:"w-3.5 h-3.5"}),s(`entra:conflicts.types.${a.conflictType}`)]})]})}),e.jsxs("div",{className:"flex-1 flex flex-col p-4 space-y-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-secondary)]",children:a.conflictDescription}),e.jsx(C,{entraData:a.entraData,localData:a.localData}),e.jsxs("div",{className:"mt-auto flex items-center justify-between pt-4 border-t border-[var(--border-color)]",children:[e.jsxs("div",{className:"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)]",children:[e.jsx(l.Calendar,{className:"w-3.5 h-3.5"}),s("entra:conflicts.createdAt",{date:o(a.createdAt)})]}),e.jsxs("button",{onClick:()=>r(a),className:"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-[var(--radius-button)] bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] transition-colors shadow-sm",children:[e.jsx(l.CheckCircle,{className:"w-4 h-4"}),s("entra:conflicts.resolve")]})]})]})]})}const v=500,M=a=>{switch(a){case"primary":return"bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] border-transparent";case"success":return"bg-[var(--success-bg)] text-[var(--success-text)] hover:opacity-90 border-[var(--success-border)]";case"secondary":return"bg-[var(--bg-secondary)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] border-[var(--border-color)]";case"ghost":return"bg-transparent text-[var(--text-secondary)] hover:bg-[var(--bg-secondary)] border-[var(--border-color)]";default:return"bg-[var(--bg-secondary)] text-[var(--text-primary)] border-[var(--border-color)]"}};function $({conflict:a,onClose:r,onResolve:t,resolving:s=!1}){const{t:o}=h.useTranslation(["entra"]),[i,d]=u.useState(""),n=[{key:"EntraPriority",icon:l.Cloud,label:o("entra:conflicts.resolutions.entraWins"),variant:"primary"},{key:"LocalPriority",icon:l.Users,label:o("entra:conflicts.resolutions.localWins"),variant:"success"},{key:"MergeToExisting",icon:l.Merge,label:o("entra:conflicts.resolutions.merge"),variant:"secondary"},{key:"SkipImport",icon:l.SkipForward,label:o("entra:conflicts.resolutions.skip"),variant:"ghost"}],m=u.useCallback(c=>{c.key==="Escape"&&!s&&r()},[r,s]);u.useEffect(()=>(document.addEventListener("keydown",m),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",m),document.body.style.overflow=""}),[m]);const x=async c=>{await t(c,i.trim()||void 0)},f=c=>{c.length<=v&&d(c)};return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm",onClick:c=>{c.target===c.currentTarget&&!s&&r()},onKeyDown:c=>{c.key==="Escape"&&!s&&r()},role:"dialog","aria-modal":"true","aria-label":"Resolve conflict",children:e.jsxs("div",{className:"w-full max-w-lg rounded-[var(--radius-card)] bg-[var(--bg-card)] shadow-2xl overflow-hidden",children:[e.jsx("div",{className:"px-6 py-4 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 rounded-lg bg-[var(--color-accent-500)]/20",children:e.jsx(l.AlertTriangle,{className:"w-5 h-5 text-[var(--color-accent-600)]"})}),e.jsx("h2",{className:"text-lg font-semibold text-[var(--text-primary)]",children:o("entra:conflicts.resolveTitle")})]}),e.jsx("button",{onClick:r,disabled:s,className:"p-2 rounded-lg text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-secondary)] transition-colors disabled:opacity-50",children:e.jsx(l.X,{className:"w-5 h-5"})})]})}),e.jsxs("div",{className:"p-6 space-y-4",children:[e.jsxs("div",{className:"p-4 rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)]",children:[e.jsx("p",{className:"font-medium text-[var(--text-primary)]",children:a.entraDisplayName}),e.jsx("p",{className:"text-sm text-[var(--text-secondary)] mt-1",children:a.conflictDescription})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"block text-sm font-medium text-[var(--text-primary)]",children:o("entra:conflicts.notes")}),e.jsxs("span",{className:"text-xs text-[var(--text-tertiary)]",children:[i.length,"/",v]})]}),e.jsx("textarea",{value:i,onChange:c=>f(c.target.value),placeholder:o("entra:conflicts.notesPlaceholder"),maxLength:v,disabled:s,className:"w-full px-3 py-2.5 rounded-[var(--radius-input)] border border-[var(--border-color)] bg-[var(--bg-secondary)] text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-500)] focus:border-transparent transition-shadow resize-none disabled:opacity-50",rows:3})]}),e.jsx("div",{className:"space-y-2",children:n.map(c=>e.jsxs("button",{onClick:()=>x(c.key),disabled:s,className:`w-full flex items-center gap-3 px-4 py-3 rounded-[var(--radius-button)] border font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed ${M(c.variant)}`,children:[s?e.jsx(l.Loader2,{className:"w-5 h-5 animate-spin"}):e.jsx(c.icon,{className:"w-5 h-5"}),e.jsx("span",{children:c.label})]},c.key))})]})]})})}exports.ConflictCard=E;exports.ResolutionModal=$;exports.createEntraApi=g;exports.entraApi=w;
|
|
2
|
-
//# sourceMappingURL=ResolutionModal-
|
|
1
|
+
"use strict";const p=require("./index-IgLVXPg8.js"),e=require("react/jsx-runtime"),h=require("react-i18next"),l=require("lucide-react"),N=require("./formatPhone-D4luHK-2.js"),u=require("react"),j=a=>({get:(r,t)=>p.apiClient.get(r,{...t,headers:a?{...t?.headers,"X-Tenant-Slug":a}:t?.headers}).then(s=>s.data),post:(r,t,s)=>p.apiClient.post(r,t,{...s,headers:a?{...s?.headers,"X-Tenant-Slug":a}:s?.headers}).then(o=>o.data),put:(r,t,s)=>p.apiClient.put(r,t,{...s,headers:a?{...s?.headers,"X-Tenant-Slug":a}:s?.headers}).then(o=>o.data),delete:(r,t)=>p.apiClient.delete(r,{...t,headers:a?{...t?.headers,"X-Tenant-Slug":a}:t?.headers}).then(s=>s.data)}),g=a=>{const r=a?j(a):p.api;return{settings:{getConfiguration:()=>r.get("/api/administration/entra/configuration"),testConnection:t=>r.post("/api/administration/entra/test-connection",t),testStoredConnection:()=>r.post("/api/administration/entra/test-stored-connection"),saveCredentials:t=>r.put("/api/administration/entra/configuration/credentials",t),saveSyncSettings:t=>r.put("/api/administration/entra/configuration/sync-settings",t)},dashboard:{getSyncStatus:()=>r.get("/api/administration/entra/sync/status")},sync:{syncAll:t=>r.post("/api/administration/entra/sync",t),syncUsers:t=>r.post("/api/administration/entra/sync/users",t),syncGroups:t=>r.post("/api/administration/entra/sync/groups",t),syncMemberships:()=>r.post("/api/administration/entra/sync/memberships"),forceFullSync:()=>r.post("/api/administration/entra/sync/force-full"),getHistory:(t=1,s=10)=>r.get(`/api/administration/entra/sync/history?page=${t}&pageSize=${s}`),cancelSession:t=>r.post(`/api/administration/entra/sync/session/${t}/cancel`)},groups:{getAll:()=>r.get("/api/administration/entra/groups"),getById:t=>r.get(`/api/administration/entra/groups/${t}`),assignRole:(t,s)=>r.post(`/api/administration/entra/groups/${t}/roles`,s),removeRole:(t,s)=>r.delete(`/api/administration/entra/groups/${t}/roles/${s}`),getMembers:t=>r.get(`/api/administration/entra/groups/${t}/members`)},users:{getGroups:t=>r.get(`/api/administration/entra/users/${t}/groups`)},conflicts:{getAll:()=>r.get("/api/administration/entra/conflicts"),resolve:(t,s)=>r.post(`/api/administration/entra/conflicts/${t}/resolve`,s)}}},w=g(),y=a=>a==null||a===""?null:a.trim(),b=(a,r=!1)=>a?r&&N.formatPhoneNumber(a,"CH","international")||a:"-";function C({entraData:a,localData:r}){const{t}=h.useTranslation(["entra","common"]);if(!a&&!r)return null;const s={firstName:t("entra:conflicts.fields.firstName","First Name"),lastName:t("entra:conflicts.fields.lastName","Last Name"),displayName:t("entra:conflicts.fields.displayName","Display Name"),email:t("entra:conflicts.fields.upn","UPN"),phoneNumber:t("entra:conflicts.fields.phoneNumber","Phone"),mobilePhone:t("entra:conflicts.fields.mobilePhone","Mobile"),department:t("entra:conflicts.fields.department","Department"),jobTitle:t("entra:conflicts.fields.jobTitle","Job Title")},o=["phoneNumber","mobilePhone"],i=Object.keys(s).map(n=>{const m=y(a?.[n]),x=y(r?.[n]),f=m!==x&&(m!==null||x!==null);return{key:n,label:s[n],entraValue:m,localValue:x,isDifferent:f}});return i.some(n=>n.isDifferent)?e.jsxs("div",{className:"mt-4 pt-4 border-t border-[var(--border-color)]",children:[e.jsx("div",{className:"overflow-hidden rounded-[var(--radius-card)] border border-[var(--border-color)]",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"bg-[var(--bg-secondary)]",children:[e.jsx("th",{className:"px-4 py-2 text-left font-medium text-[var(--text-secondary)] w-1/4",children:t("entra:conflicts.field","Field")}),e.jsx("th",{className:"px-4 py-2 text-left font-medium text-[var(--color-accent-600)] w-[37.5%]",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(l.Cloud,{className:"w-4 h-4"}),t("entra:conflicts.entraData","Entra")]})}),e.jsx("th",{className:"px-4 py-2 text-left font-medium text-[var(--success-text)] w-[37.5%]",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(l.Users,{className:"w-4 h-4"}),t("entra:conflicts.localData","Local")]})})]})}),e.jsx("tbody",{className:"divide-y divide-[var(--border-color)]",children:i.filter(n=>n.isDifferent).map(n=>e.jsxs("tr",{className:"bg-[var(--bg-card)] hover:bg-[var(--bg-secondary)] transition-colors",children:[e.jsx("td",{className:"px-4 py-3 font-medium text-[var(--text-primary)]",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(l.AlertCircle,{className:"w-4 h-4 text-[var(--color-accent-500)]"}),n.label]})}),e.jsx("td",{className:"px-4 py-3",children:n.entraValue?e.jsx("span",{className:"text-[var(--color-accent-700)] bg-[var(--color-accent-500)]/10 px-2 py-1 rounded",children:b(n.entraValue,o.includes(n.key))}):e.jsx("span",{className:"text-[var(--text-tertiary)] italic",children:b(n.entraValue,o.includes(n.key))})}),e.jsx("td",{className:"px-4 py-3",children:n.localValue?e.jsx("span",{className:"text-[var(--success-text)] bg-[var(--success-bg)] px-2 py-1 rounded",children:b(n.localValue,o.includes(n.key))}):e.jsx("span",{className:"text-[var(--text-tertiary)] italic",children:b(n.localValue,o.includes(n.key))})})]},n.key))})]})}),e.jsx("p",{className:"mt-2 text-xs text-[var(--text-tertiary)]",children:t("entra:conflicts.diffCount","{{count}} field(s) with differences",{count:i.filter(n=>n.isDifferent).length})})]}):e.jsx("div",{className:"mt-4 pt-4 border-t border-[var(--border-color)]",children:e.jsx("p",{className:"text-sm text-[var(--text-secondary)] italic",children:t("entra:conflicts.noDifferences","No field differences detected")})})}function k(){const{i18n:a}=h.useTranslation(),r=u.useMemo(()=>({fr:"fr-FR",en:"en-US",de:"de-DE",it:"it-IT"})[a.language]||"en-US",[a.language]),t=u.useMemo(()=>(i,d)=>{const n={dateStyle:"medium",timeStyle:"short"};return new Intl.DateTimeFormat(r,d||n).format(typeof i=="string"?new Date(i):i)},[r]),s=u.useMemo(()=>(i,d)=>new Intl.NumberFormat(r,d).format(i),[r]),o=u.useMemo(()=>(i,d="EUR")=>new Intl.NumberFormat(r,{style:"currency",currency:d}).format(i),[r]);return{locale:r,formatDate:t,formatNumber:s,formatCurrency:o}}const D=a=>{switch(a){case"User":return l.Users;case"Group":return l.Building2;case"Membership":return l.Link2;default:return l.Cloud}},T=a=>a==="Duplicate"?{bg:"bg-[var(--error-bg)]",text:"text-[var(--error-text)]",border:"border-[var(--error-border)]"}:{bg:"bg-[var(--color-accent-500)]/10",text:"text-[var(--color-accent-700)]",border:"border-[var(--color-accent-300)]"};function E({conflict:a,onResolve:r,className:t=""}){const{t:s}=h.useTranslation(["entra"]),{formatDate:o}=k(),i=D(a.resourceType),d=T(a.conflictType);return e.jsxs("div",{className:`h-full flex flex-col rounded-[var(--radius-card)] border border-[var(--border-color)] bg-[var(--bg-card)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${t}`,children:[e.jsx("div",{className:"px-4 py-3 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 rounded-lg bg-[var(--color-accent-500)]/20",children:e.jsx(i,{className:"w-5 h-5 text-[var(--color-accent-600)]"})}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-[var(--text-primary)]",children:a.entraDisplayName}),a.entraEmail&&e.jsxs("div",{className:"flex items-center gap-1 text-sm text-[var(--text-secondary)]",children:[e.jsx(l.Mail,{className:"w-3.5 h-3.5"}),a.entraEmail]})]})]}),e.jsxs("span",{className:`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${d.bg} ${d.text} border ${d.border}`,children:[e.jsx(l.AlertTriangle,{className:"w-3.5 h-3.5"}),s(`entra:conflicts.types.${a.conflictType}`)]})]})}),e.jsxs("div",{className:"flex-1 flex flex-col p-4 space-y-4",children:[e.jsx("p",{className:"text-sm text-[var(--text-secondary)]",children:a.conflictDescription}),e.jsx(C,{entraData:a.entraData,localData:a.localData}),e.jsxs("div",{className:"mt-auto flex items-center justify-between pt-4 border-t border-[var(--border-color)]",children:[e.jsxs("div",{className:"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)]",children:[e.jsx(l.Calendar,{className:"w-3.5 h-3.5"}),s("entra:conflicts.createdAt",{date:o(a.createdAt)})]}),e.jsxs("button",{onClick:()=>r(a),className:"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-[var(--radius-button)] bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] transition-colors shadow-sm",children:[e.jsx(l.CheckCircle,{className:"w-4 h-4"}),s("entra:conflicts.resolve")]})]})]})]})}const v=500,M=a=>{switch(a){case"primary":return"bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] border-transparent";case"success":return"bg-[var(--success-bg)] text-[var(--success-text)] hover:opacity-90 border-[var(--success-border)]";case"secondary":return"bg-[var(--bg-secondary)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] border-[var(--border-color)]";case"ghost":return"bg-transparent text-[var(--text-secondary)] hover:bg-[var(--bg-secondary)] border-[var(--border-color)]";default:return"bg-[var(--bg-secondary)] text-[var(--text-primary)] border-[var(--border-color)]"}};function $({conflict:a,onClose:r,onResolve:t,resolving:s=!1}){const{t:o}=h.useTranslation(["entra"]),[i,d]=u.useState(""),n=[{key:"EntraPriority",icon:l.Cloud,label:o("entra:conflicts.resolutions.entraWins"),variant:"primary"},{key:"LocalPriority",icon:l.Users,label:o("entra:conflicts.resolutions.localWins"),variant:"success"},{key:"MergeToExisting",icon:l.Merge,label:o("entra:conflicts.resolutions.merge"),variant:"secondary"},{key:"SkipImport",icon:l.SkipForward,label:o("entra:conflicts.resolutions.skip"),variant:"ghost"}],m=u.useCallback(c=>{c.key==="Escape"&&!s&&r()},[r,s]);u.useEffect(()=>(document.addEventListener("keydown",m),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",m),document.body.style.overflow=""}),[m]);const x=async c=>{await t(c,i.trim()||void 0)},f=c=>{c.length<=v&&d(c)};return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm",onClick:c=>{c.target===c.currentTarget&&!s&&r()},onKeyDown:c=>{c.key==="Escape"&&!s&&r()},role:"dialog","aria-modal":"true","aria-label":"Resolve conflict",children:e.jsxs("div",{className:"w-full max-w-lg rounded-[var(--radius-card)] bg-[var(--bg-card)] shadow-2xl overflow-hidden",children:[e.jsx("div",{className:"px-6 py-4 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 rounded-lg bg-[var(--color-accent-500)]/20",children:e.jsx(l.AlertTriangle,{className:"w-5 h-5 text-[var(--color-accent-600)]"})}),e.jsx("h2",{className:"text-lg font-semibold text-[var(--text-primary)]",children:o("entra:conflicts.resolveTitle")})]}),e.jsx("button",{onClick:r,disabled:s,className:"p-2 rounded-lg text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-secondary)] transition-colors disabled:opacity-50",children:e.jsx(l.X,{className:"w-5 h-5"})})]})}),e.jsxs("div",{className:"p-6 space-y-4",children:[e.jsxs("div",{className:"p-4 rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)]",children:[e.jsx("p",{className:"font-medium text-[var(--text-primary)]",children:a.entraDisplayName}),e.jsx("p",{className:"text-sm text-[var(--text-secondary)] mt-1",children:a.conflictDescription})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("label",{className:"block text-sm font-medium text-[var(--text-primary)]",children:o("entra:conflicts.notes")}),e.jsxs("span",{className:"text-xs text-[var(--text-tertiary)]",children:[i.length,"/",v]})]}),e.jsx("textarea",{value:i,onChange:c=>f(c.target.value),placeholder:o("entra:conflicts.notesPlaceholder"),maxLength:v,disabled:s,className:"w-full px-3 py-2.5 rounded-[var(--radius-input)] border border-[var(--border-color)] bg-[var(--bg-secondary)] text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-500)] focus:border-transparent transition-shadow resize-none disabled:opacity-50",rows:3})]}),e.jsx("div",{className:"space-y-2",children:n.map(c=>e.jsxs("button",{onClick:()=>x(c.key),disabled:s,className:`w-full flex items-center gap-3 px-4 py-3 rounded-[var(--radius-button)] border font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed ${M(c.variant)}`,children:[s?e.jsx(l.Loader2,{className:"w-5 h-5 animate-spin"}):e.jsx(c.icon,{className:"w-5 h-5"}),e.jsx("span",{children:c.label})]},c.key))})]})]})})}exports.ConflictCard=E;exports.ResolutionModal=$;exports.createEntraApi=g;exports.entraApi=w;
|
|
2
|
+
//# sourceMappingURL=ResolutionModal-Duat18qV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResolutionModal-DqRk_T0n.js","sources":["../../src/services/api/entraApi.ts","../../src/components/platform/administration/ConflictDiffView.tsx","../../src/hooks/useLocale.ts","../../src/components/platform/administration/entra/ConflictCard.tsx","../../src/components/platform/administration/entra/ResolutionModal.tsx"],"sourcesContent":["import { api, apiClient } from './apiClient';\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) => ({\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 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\n// Types for Entra Dashboard/Sync Status\r\nexport interface EntraSyncStatusDto {\r\n resourceType: string;\r\n status: string;\r\n lastFullSyncAt: string | null;\r\n lastDeltaSyncAt: string | null;\r\n totalItemsSynced: number;\r\n pendingConflicts: number;\r\n lastError: string | null;\r\n}\r\n\r\nexport interface EntraSyncResultDto {\r\n syncSessionId: string;\r\n resourceType: string;\r\n success: boolean;\r\n errorMessage: string | null;\r\n startedAt: string;\r\n completedAt: string;\r\n totalProcessed: number;\r\n created: number;\r\n updated: number;\r\n deleted: number;\r\n skipped: number;\r\n conflicts: number;\r\n pendingConflicts: EntraSyncConflictDto[];\r\n}\r\n\r\nexport interface EntraSyncOptionsDto {\r\n useDeltaSync?: boolean;\r\n syncUsers?: boolean;\r\n syncGroups?: boolean;\r\n syncMemberships?: boolean;\r\n conflictPriority?: 'ManualReview' | 'EntraWins' | 'LocalWins';\r\n autoCreateReferences?: boolean;\r\n}\r\n\r\n// Types for Entra Sync History\r\nexport interface EntraSyncSessionDto {\r\n id: string;\r\n resourceType: string;\r\n isFullSync: boolean;\r\n status: string;\r\n startedAt: string;\r\n completedAt: string | null;\r\n errorMessage: string | null;\r\n totalProcessed: number;\r\n created: number;\r\n updated: number;\r\n deleted: number;\r\n skipped: number;\r\n conflicts: number;\r\n triggerType: string;\r\n triggeredByUserId: string | null;\r\n triggeredByUserName: string | null;\r\n duration: string | null;\r\n}\r\n\r\nexport interface EntraSyncHistoryResponse {\r\n sessions: EntraSyncSessionDto[];\r\n totalCount: number;\r\n page: number;\r\n pageSize: number;\r\n}\r\n\r\n// Types for Entra Conflicts\r\nexport interface ConflictDataDto {\r\n firstName: string | null;\r\n lastName: string | null;\r\n displayName: string | null;\r\n email: string | null;\r\n phoneNumber: string | null;\r\n mobilePhone: string | null;\r\n department: string | null;\r\n jobTitle: string | null;\r\n}\r\n\r\nexport interface EntraSyncConflictDto {\r\n id: string;\r\n resourceType: string;\r\n entraObjectId: string;\r\n entraDisplayName: string;\r\n entraEmail: string | null;\r\n existingLocalEntityId: string | null;\r\n existingLocalDisplayName: string | null;\r\n conflictType: string;\r\n conflictDescription: string;\r\n resolution: string;\r\n createdAt: string;\r\n entraData: ConflictDataDto | null;\r\n localData: ConflictDataDto | null;\r\n}\r\n\r\nexport interface ResolveConflictRequest {\r\n resolution: 'EntraPriority' | 'LocalPriority' | 'SkipImport' | 'MergeToExisting' | 'CreateNew';\r\n notes?: string;\r\n}\r\n\r\n// Types for Entra Groups\r\nexport interface EntraGroupDto {\r\n id: string;\r\n entraObjectId: string;\r\n displayName: string;\r\n description: string | null;\r\n mail: string | null;\r\n isSecurityGroup: boolean;\r\n isActive: boolean;\r\n lastSyncedAt: string;\r\n memberCount: number;\r\n assignedRoles: EntraGroupRoleDto[];\r\n /** Roles inherited from parent groups in the hierarchy */\r\n inheritedRoles: InheritedRoleDto[];\r\n // Parent-child relationship for nested groups\r\n parentGroupId: string | null;\r\n parentGroupName: string | null;\r\n childGroupCount: number;\r\n childGroups: EntraGroupSummaryDto[];\r\n}\r\n\r\nexport interface EntraGroupSummaryDto {\r\n id: string;\r\n entraObjectId: string;\r\n displayName: string;\r\n isSecurityGroup: boolean;\r\n memberCount: number;\r\n childGroupCount: number;\r\n}\r\n\r\nexport interface EntraGroupRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n assignedAt: string;\r\n assignedBy: string;\r\n /** If true, this role is inherited by child groups in the hierarchy */\r\n isInheritable: boolean;\r\n}\r\n\r\n/**\r\n * Represents a role inherited from a parent group in the hierarchy\r\n */\r\nexport interface InheritedRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n assignedAt: string;\r\n assignedBy: string;\r\n /** The source group from which this role is inherited */\r\n sourceGroupId: string;\r\n sourceGroupName: string;\r\n /** How many levels up in the hierarchy (1 = direct parent) */\r\n inheritanceLevel: number;\r\n}\r\n\r\nexport interface EntraGroupMemberDto {\r\n userId: string;\r\n fullName: string;\r\n email: string;\r\n entraObjectId: string | null;\r\n isActive: boolean;\r\n addedAt: string;\r\n /** True if the member is directly in this group, false if via a child group */\r\n isDirect: boolean;\r\n /** Source group ID if member comes from a child group (null if direct) */\r\n sourceGroupId: string | null;\r\n /** Source group name if member comes from a child group */\r\n sourceGroupName: string | null;\r\n}\r\n\r\nexport interface AssignGroupToRoleRequest {\r\n roleId: string;\r\n /** If true, this role will be inherited by child groups. Default: true */\r\n isInheritable?: boolean;\r\n}\r\n\r\n// Types for Connection Test\r\nexport interface TestConnectionRequest {\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface TestConnectionResult {\r\n success: boolean;\r\n errorMessage: string | null;\r\n tenantName: string | null;\r\n userCount: number | null;\r\n groupCount: number | null;\r\n}\r\n\r\n// Types for Configuration\r\nexport interface EntraConfigurationDto {\r\n tenantId: string | null;\r\n clientId: string | null;\r\n hasClientSecret: boolean;\r\n syncEnabled: boolean;\r\n syncIntervalMinutes: number;\r\n useDeltaSync: boolean;\r\n defaultConflictResolution: string;\r\n autoCreateReferences: boolean;\r\n isConfigured: boolean;\r\n // User sync filters\r\n syncOnlyActiveUsers: boolean;\r\n /** Comma-separated list of user types to sync: member, guest, shared, room, equipment */\r\n syncUserTypes: string;\r\n}\r\n\r\nexport interface SaveEntraCredentialsRequest {\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface SaveEntraSyncSettingsRequest {\r\n syncEnabled: boolean;\r\n syncIntervalMinutes: number;\r\n useDeltaSync: boolean;\r\n defaultConflictResolution: string;\r\n autoCreateReferences: boolean;\r\n // User sync filters\r\n syncOnlyActiveUsers?: boolean;\r\n /** Comma-separated list of user types to sync: member, guest, shared, room, equipment */\r\n syncUserTypes?: string;\r\n}\r\n\r\n/**\r\n * Creates the Entra 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 createEntraApi = (tenantSlug?: string) => {\r\n const apiContext = tenantSlug ? withTenantContext(tenantSlug) : api;\r\n\r\n return {\r\n // Settings / Configuration\r\n settings: {\r\n /**\r\n * Get current Entra configuration\r\n */\r\n getConfiguration: () =>\r\n apiContext.get<EntraConfigurationDto>('/api/administration/entra/configuration'),\r\n\r\n /**\r\n * Test connection to Microsoft Entra ID with provided credentials\r\n */\r\n testConnection: (request: TestConnectionRequest) =>\r\n apiContext.post<TestConnectionResult>('/api/administration/entra/test-connection', request),\r\n\r\n /**\r\n * Test connection using stored credentials from database\r\n */\r\n testStoredConnection: () =>\r\n apiContext.post<TestConnectionResult>('/api/administration/entra/test-stored-connection'),\r\n\r\n /**\r\n * Save Entra credentials (TenantId, ClientId, ClientSecret)\r\n */\r\n saveCredentials: (request: SaveEntraCredentialsRequest) =>\r\n apiContext.put<{ message: string }>('/api/administration/entra/configuration/credentials', request),\r\n\r\n /**\r\n * Save Entra sync settings\r\n */\r\n saveSyncSettings: (request: SaveEntraSyncSettingsRequest) =>\r\n apiContext.put<{ message: string }>('/api/administration/entra/configuration/sync-settings', request),\r\n },\r\n\r\n // Dashboard / Sync Status\r\n dashboard: {\r\n /**\r\n * Get sync status for all resource types (Users, Groups, Memberships)\r\n */\r\n getSyncStatus: () =>\r\n apiContext.get<EntraSyncStatusDto[]>('/api/administration/entra/sync/status'),\r\n },\r\n\r\n // Sync Operations\r\n sync: {\r\n /**\r\n * Trigger full synchronization of all resources\r\n */\r\n syncAll: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync', options),\r\n\r\n /**\r\n * Trigger users synchronization only\r\n */\r\n syncUsers: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/users', options),\r\n\r\n /**\r\n * Trigger groups synchronization only\r\n */\r\n syncGroups: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/groups', options),\r\n\r\n /**\r\n * Trigger memberships synchronization only\r\n */\r\n syncMemberships: () =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/memberships'),\r\n\r\n /**\r\n * Force full sync (ignores delta tokens, re-syncs everything)\r\n */\r\n forceFullSync: () =>\r\n apiContext.post<{ message: string }>('/api/administration/entra/sync/force-full'),\r\n\r\n /**\r\n * Get sync history with pagination\r\n */\r\n getHistory: (page: number = 1, pageSize: number = 10) =>\r\n apiContext.get<EntraSyncHistoryResponse>(`/api/administration/entra/sync/history?page=${page}&pageSize=${pageSize}`),\r\n\r\n /**\r\n * Cancel a running sync session\r\n */\r\n cancelSession: (sessionId: string) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/sync/session/${sessionId}/cancel`),\r\n },\r\n\r\n // Groups Management\r\n groups: {\r\n /**\r\n * Get all Entra groups\r\n */\r\n getAll: () =>\r\n apiContext.get<EntraGroupDto[]>('/api/administration/entra/groups'),\r\n\r\n /**\r\n * Get a specific Entra group by ID\r\n */\r\n getById: (groupId: string) =>\r\n apiContext.get<EntraGroupDto>(`/api/administration/entra/groups/${groupId}`),\r\n\r\n /**\r\n * Assign a role to an Entra group\r\n */\r\n assignRole: (groupId: string, request: AssignGroupToRoleRequest) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/groups/${groupId}/roles`, request),\r\n\r\n /**\r\n * Remove a role from an Entra group\r\n */\r\n removeRole: (groupId: string, roleId: string) =>\r\n apiContext.delete<{ message: string }>(`/api/administration/entra/groups/${groupId}/roles/${roleId}`),\r\n\r\n /**\r\n * Get members of an Entra group\r\n */\r\n getMembers: (groupId: string) =>\r\n apiContext.get<EntraGroupMemberDto[]>(`/api/administration/entra/groups/${groupId}/members`),\r\n },\r\n\r\n // Users Management (Entra-related)\r\n users: {\r\n /**\r\n * Get Entra groups for a specific user\r\n */\r\n getGroups: (userId: string) =>\r\n apiContext.get<EntraGroupDto[]>(`/api/administration/entra/users/${userId}/groups`),\r\n },\r\n\r\n // Conflicts Management\r\n conflicts: {\r\n /**\r\n * Get all pending sync conflicts\r\n */\r\n getAll: () =>\r\n apiContext.get<EntraSyncConflictDto[]>('/api/administration/entra/conflicts'),\r\n\r\n /**\r\n * Resolve a specific conflict\r\n */\r\n resolve: (conflictId: string, data: ResolveConflictRequest) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/conflicts/${conflictId}/resolve`, data),\r\n },\r\n };\r\n};\r\n\r\n// Default export uses localStorage tenant slug (backward compatible)\r\nexport const entraApi = createEntraApi();\r\n\r\n// Export factory for explicit tenant context\r\nexport { createEntraApi };\r\n","import type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Cloud, Users, AlertCircle } from 'lucide-react';\r\nimport type { ConflictDataDto } from '@/services/api/entraApi';\r\nimport { formatPhoneNumber } from '@/utils/formatPhone';\r\n\r\ninterface ConflictDiffViewProps {\r\n readonly entraData: ConflictDataDto | null;\r\n readonly localData: ConflictDataDto | null;\r\n}\r\n\r\ninterface FieldDiff {\r\n key: string;\r\n label: string;\r\n entraValue: string | null;\r\n localValue: string | null;\r\n isDifferent: boolean;\r\n}\r\n\r\nconst normalizeValue = (value: string | null | undefined): string | null => {\r\n if (value === null || value === undefined || value === '') return null;\r\n return value.trim();\r\n};\r\n\r\nconst formatValue = (value: string | null, isPhone: boolean = false): string => {\r\n if (!value) return '-';\r\n if (isPhone) {\r\n const formatted = formatPhoneNumber(value, 'CH', 'international');\r\n return formatted || value;\r\n }\r\n return value;\r\n};\r\n\r\nexport function ConflictDiffView({ entraData, localData }: ConflictDiffViewProps): ReactElement | null {\r\n const { t } = useTranslation(['entra', 'common']);\r\n\r\n if (!entraData && !localData) {\r\n return null;\r\n }\r\n\r\n const fieldLabels: Record<string, string> = {\r\n firstName: t('entra:conflicts.fields.firstName', 'First Name'),\r\n lastName: t('entra:conflicts.fields.lastName', 'Last Name'),\r\n displayName: t('entra:conflicts.fields.displayName', 'Display Name'),\r\n email: t('entra:conflicts.fields.upn', 'UPN'),\r\n phoneNumber: t('entra:conflicts.fields.phoneNumber', 'Phone'),\r\n mobilePhone: t('entra:conflicts.fields.mobilePhone', 'Mobile'),\r\n department: t('entra:conflicts.fields.department', 'Department'),\r\n jobTitle: t('entra:conflicts.fields.jobTitle', 'Job Title'),\r\n };\r\n\r\n const phoneFields = ['phoneNumber', 'mobilePhone'];\r\n\r\n const fields: FieldDiff[] = Object.keys(fieldLabels).map((key) => {\r\n const entraValue = normalizeValue(entraData?.[key as keyof ConflictDataDto]);\r\n const localValue = normalizeValue(localData?.[key as keyof ConflictDataDto]);\r\n const isDifferent = entraValue !== localValue && (entraValue !== null || localValue !== null);\r\n\r\n return {\r\n key,\r\n label: fieldLabels[key],\r\n entraValue,\r\n localValue,\r\n isDifferent,\r\n };\r\n });\r\n\r\n const hasDifferences = fields.some((f) => f.isDifferent);\r\n\r\n if (!hasDifferences) {\r\n return (\r\n <div className=\"mt-4 pt-4 border-t border-[var(--border-color)]\">\r\n <p className=\"text-sm text-[var(--text-secondary)] italic\">\r\n {t('entra:conflicts.noDifferences', 'No field differences detected')}\r\n </p>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"mt-4 pt-4 border-t border-[var(--border-color)]\">\r\n <div className=\"overflow-hidden rounded-[var(--radius-card)] border border-[var(--border-color)]\">\r\n <table className=\"w-full text-sm\">\r\n <thead>\r\n <tr className=\"bg-[var(--bg-secondary)]\">\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--text-secondary)] w-1/4\">\r\n {t('entra:conflicts.field', 'Field')}\r\n </th>\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--color-accent-600)] w-[37.5%]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Cloud className=\"w-4 h-4\" />\r\n {t('entra:conflicts.entraData', 'Entra')}\r\n </div>\r\n </th>\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--success-text)] w-[37.5%]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Users className=\"w-4 h-4\" />\r\n {t('entra:conflicts.localData', 'Local')}\r\n </div>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody className=\"divide-y divide-[var(--border-color)]\">\r\n {fields\r\n .filter((f) => f.isDifferent)\r\n .map((field) => (\r\n <tr\r\n key={field.key}\r\n className=\"bg-[var(--bg-card)] hover:bg-[var(--bg-secondary)] transition-colors\"\r\n >\r\n <td className=\"px-4 py-3 font-medium text-[var(--text-primary)]\">\r\n <div className=\"flex items-center gap-2\">\r\n <AlertCircle className=\"w-4 h-4 text-[var(--color-accent-500)]\" />\r\n {field.label}\r\n </div>\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n {field.entraValue ? (\r\n <span className=\"text-[var(--color-accent-700)] bg-[var(--color-accent-500)]/10 px-2 py-1 rounded\">\r\n {formatValue(field.entraValue, phoneFields.includes(field.key))}\r\n </span>\r\n ) : (\r\n <span className=\"text-[var(--text-tertiary)] italic\">\r\n {formatValue(field.entraValue, phoneFields.includes(field.key))}\r\n </span>\r\n )}\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n {field.localValue ? (\r\n <span className=\"text-[var(--success-text)] bg-[var(--success-bg)] px-2 py-1 rounded\">\r\n {formatValue(field.localValue, phoneFields.includes(field.key))}\r\n </span>\r\n ) : (\r\n <span className=\"text-[var(--text-tertiary)] italic\">\r\n {formatValue(field.localValue, phoneFields.includes(field.key))}\r\n </span>\r\n )}\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n <p className=\"mt-2 text-xs text-[var(--text-tertiary)]\">\r\n {t('entra:conflicts.diffCount', '{{count}} field(s) with differences', {\r\n count: fields.filter((f) => f.isDifferent).length,\r\n })}\r\n </p>\r\n </div>\r\n );\r\n}\r\n","import { useMemo } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\n\r\n/**\r\n * Hook for locale-aware formatting utilities.\r\n * Automatically uses the current i18n language for formatting.\r\n */\r\nexport function useLocale(): {\n locale: string;\n formatDate: (date: string | Date, options?: Intl.DateTimeFormatOptions) => string;\n formatNumber: (value: number, options?: Intl.NumberFormatOptions) => string;\n formatCurrency: (value: number, currency?: string) => string;\n} {\r\n const { i18n } = useTranslation();\r\n\r\n const locale = useMemo(() => {\r\n // Map i18n language codes to Intl locale codes\r\n const localeMap: Record<string, string> = {\r\n fr: 'fr-FR',\r\n en: 'en-US',\r\n de: 'de-DE',\r\n it: 'it-IT',\r\n };\r\n return localeMap[i18n.language] || 'en-US';\r\n }, [i18n.language]);\r\n\r\n const formatDate = useMemo(() => {\r\n return (date: string | Date, options?: Intl.DateTimeFormatOptions) => {\r\n const defaultOptions: Intl.DateTimeFormatOptions = {\r\n dateStyle: 'medium',\r\n timeStyle: 'short',\r\n };\r\n return new Intl.DateTimeFormat(locale, options || defaultOptions).format(\r\n typeof date === 'string' ? new Date(date) : date\r\n );\r\n };\r\n }, [locale]);\r\n\r\n const formatNumber = useMemo(() => {\r\n return (value: number, options?: Intl.NumberFormatOptions) => {\r\n return new Intl.NumberFormat(locale, options).format(value);\r\n };\r\n }, [locale]);\r\n\r\n const formatCurrency = useMemo(() => {\r\n return (value: number, currency = 'EUR') => {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n }).format(value);\r\n };\r\n }, [locale]);\r\n\r\n return {\r\n locale,\r\n formatDate,\r\n formatNumber,\r\n formatCurrency,\r\n };\r\n}\r\n","import type { ReactElement } from 'react';\nimport { useTranslation } from 'react-i18next';\r\nimport {\r\n AlertTriangle,\r\n CheckCircle,\r\n Users,\r\n Building2,\r\n Link2,\r\n Cloud,\r\n Calendar,\r\n Mail,\r\n} from 'lucide-react';\r\nimport type { EntraSyncConflictDto } from '@/services/api/entraApi';\r\nimport { ConflictDiffView } from '@/components/platform/administration/ConflictDiffView';\r\nimport { useLocale } from '@/hooks/useLocale';\r\n\r\nexport interface ConflictCardProps {\r\n readonly conflict: EntraSyncConflictDto;\r\n readonly onResolve: (conflict: EntraSyncConflictDto) => void;\r\n readonly className?: string;\r\n}\r\n\r\nconst getResourceIcon = (resourceType: string) => {\r\n switch (resourceType) {\r\n case 'User':\r\n return Users;\r\n case 'Group':\r\n return Building2;\r\n case 'Membership':\r\n return Link2;\r\n default:\r\n return Cloud;\r\n }\r\n};\r\n\r\nconst getConflictTypeConfig = (conflictType: string) => {\r\n switch (conflictType) {\r\n case 'Duplicate':\r\n return {\r\n bg: 'bg-[var(--error-bg)]',\r\n text: 'text-[var(--error-text)]',\r\n border: 'border-[var(--error-border)]',\r\n };\r\n default:\r\n return {\r\n bg: 'bg-[var(--color-accent-500)]/10',\r\n text: 'text-[var(--color-accent-700)]',\r\n border: 'border-[var(--color-accent-300)]',\r\n };\r\n }\r\n};\r\n\r\nexport function ConflictCard({ conflict, onResolve, className = '' }: ConflictCardProps): ReactElement {\r\n const { t } = useTranslation(['entra']);\r\n const { formatDate } = useLocale();\r\n\r\n const ResourceIcon = getResourceIcon(conflict.resourceType);\r\n const typeConfig = getConflictTypeConfig(conflict.conflictType);\r\n\r\n return (\r\n <div className={`h-full flex flex-col rounded-[var(--radius-card)] border border-[var(--border-color)] bg-[var(--bg-card)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`}>\r\n {/* Header */}\r\n <div className=\"px-4 py-3 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-500)]/20\">\r\n <ResourceIcon className=\"w-5 h-5 text-[var(--color-accent-600)]\" />\r\n </div>\r\n <div>\r\n <h3 className=\"font-semibold text-[var(--text-primary)]\">\r\n {conflict.entraDisplayName}\r\n </h3>\r\n {conflict.entraEmail && (\r\n <div className=\"flex items-center gap-1 text-sm text-[var(--text-secondary)]\">\r\n <Mail className=\"w-3.5 h-3.5\" />\r\n {conflict.entraEmail}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${typeConfig.bg} ${typeConfig.text} border ${typeConfig.border}`}>\r\n <AlertTriangle className=\"w-3.5 h-3.5\" />\r\n {t(`entra:conflicts.types.${conflict.conflictType}`)}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 flex flex-col p-4 space-y-4\">\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {conflict.conflictDescription}\r\n </p>\r\n\r\n <ConflictDiffView entraData={conflict.entraData} localData={conflict.localData} />\r\n\r\n {/* Footer */}\r\n <div className=\"mt-auto flex items-center justify-between pt-4 border-t border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)]\">\r\n <Calendar className=\"w-3.5 h-3.5\" />\r\n {t('entra:conflicts.createdAt', { date: formatDate(conflict.createdAt) })}\r\n </div>\r\n <button\r\n onClick={() => onResolve(conflict)}\r\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-[var(--radius-button)] bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] transition-colors shadow-sm\"\r\n >\r\n <CheckCircle className=\"w-4 h-4\" />\r\n {t('entra:conflicts.resolve')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useEffect, useCallback, useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport {\r\n AlertTriangle,\r\n X,\r\n Loader2,\r\n Cloud,\r\n Users,\r\n Merge,\r\n SkipForward,\r\n} from 'lucide-react';\r\nimport type { EntraSyncConflictDto, ResolveConflictRequest } from '@/services/api/entraApi';\r\n\r\nconst NOTES_MAX_LENGTH = 500;\r\n\r\nexport interface ResolutionModalProps {\r\n readonly conflict: EntraSyncConflictDto;\r\n readonly onClose: () => void;\r\n readonly onResolve: (resolution: ResolveConflictRequest['resolution'], notes?: string) => Promise<void>;\r\n readonly resolving?: boolean;\r\n}\r\n\r\nconst getButtonClasses = (variant: string) => {\r\n switch (variant) {\r\n case 'primary':\r\n return 'bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] border-transparent';\r\n case 'success':\r\n return 'bg-[var(--success-bg)] text-[var(--success-text)] hover:opacity-90 border-[var(--success-border)]';\r\n case 'secondary':\r\n return 'bg-[var(--bg-secondary)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] border-[var(--border-color)]';\r\n case 'ghost':\r\n return 'bg-transparent text-[var(--text-secondary)] hover:bg-[var(--bg-secondary)] border-[var(--border-color)]';\r\n default:\r\n return 'bg-[var(--bg-secondary)] text-[var(--text-primary)] border-[var(--border-color)]';\r\n }\r\n};\r\n\r\nexport function ResolutionModal({\r\n conflict,\r\n onClose,\r\n onResolve,\r\n resolving = false,\r\n}: ResolutionModalProps): ReactElement {\r\n const { t } = useTranslation(['entra']);\r\n const [notes, setNotes] = useState('');\r\n\r\n const resolutionOptions = [\r\n {\r\n key: 'EntraPriority' as const,\r\n icon: Cloud,\r\n label: t('entra:conflicts.resolutions.entraWins'),\r\n variant: 'primary' as const,\r\n },\r\n {\r\n key: 'LocalPriority' as const,\r\n icon: Users,\r\n label: t('entra:conflicts.resolutions.localWins'),\r\n variant: 'success' as const,\r\n },\r\n {\r\n key: 'MergeToExisting' as const,\r\n icon: Merge,\r\n label: t('entra:conflicts.resolutions.merge'),\r\n variant: 'secondary' as const,\r\n },\r\n {\r\n key: 'SkipImport' as const,\r\n icon: SkipForward,\r\n label: t('entra:conflicts.resolutions.skip'),\r\n variant: 'ghost' as const,\r\n },\r\n ];\r\n\r\n // Handle escape key to close modal\r\n const handleKeyDown = useCallback((e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && !resolving) {\r\n onClose();\r\n }\r\n }, [onClose, resolving]);\r\n\r\n useEffect(() => {\r\n document.addEventListener('keydown', handleKeyDown);\r\n // Lock body scroll\r\n document.body.style.overflow = 'hidden';\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown);\r\n document.body.style.overflow = '';\r\n };\r\n }, [handleKeyDown]);\r\n\r\n const handleResolve = async (resolution: ResolveConflictRequest['resolution']) => {\r\n await onResolve(resolution, notes.trim() || undefined);\r\n };\r\n\r\n const handleNotesChange = (value: string) => {\r\n // Limit notes length\r\n if (value.length <= NOTES_MAX_LENGTH) {\r\n setNotes(value);\r\n }\r\n };\r\n\r\n return (\r\n <div\r\n className=\"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm\"\r\n onClick={(e) => {\r\n if (e.target === e.currentTarget && !resolving) {\r\n onClose();\r\n }\r\n }}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Escape' && !resolving) {\r\n onClose();\r\n }\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label=\"Resolve conflict\"\r\n >\r\n <div className=\"w-full max-w-lg rounded-[var(--radius-card)] bg-[var(--bg-card)] shadow-2xl overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"px-6 py-4 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-500)]/20\">\r\n <AlertTriangle className=\"w-5 h-5 text-[var(--color-accent-600)]\" />\r\n </div>\r\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">\r\n {t('entra:conflicts.resolveTitle')}\r\n </h2>\r\n </div>\r\n <button\r\n onClick={onClose}\r\n disabled={resolving}\r\n className=\"p-2 rounded-lg text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-secondary)] transition-colors disabled:opacity-50\"\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=\"p-6 space-y-4\">\r\n {/* Conflict Info */}\r\n <div className=\"p-4 rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)]\">\r\n <p className=\"font-medium text-[var(--text-primary)]\">{conflict.entraDisplayName}</p>\r\n <p className=\"text-sm text-[var(--text-secondary)] mt-1\">{conflict.conflictDescription}</p>\r\n </div>\r\n\r\n {/* Notes */}\r\n <div>\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <label className=\"block text-sm font-medium text-[var(--text-primary)]\">\r\n {t('entra:conflicts.notes')}\r\n </label>\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n {notes.length}/{NOTES_MAX_LENGTH}\r\n </span>\r\n </div>\r\n <textarea\r\n value={notes}\r\n onChange={(e) => handleNotesChange(e.target.value)}\r\n placeholder={t('entra:conflicts.notesPlaceholder')}\r\n maxLength={NOTES_MAX_LENGTH}\r\n disabled={resolving}\r\n className=\"w-full px-3 py-2.5 rounded-[var(--radius-input)] border border-[var(--border-color)] bg-[var(--bg-secondary)] text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-500)] focus:border-transparent transition-shadow resize-none disabled:opacity-50\"\r\n rows={3}\r\n />\r\n </div>\r\n\r\n {/* Resolution Options */}\r\n <div className=\"space-y-2\">\r\n {resolutionOptions.map((option) => (\r\n <button\r\n key={option.key}\r\n onClick={() => handleResolve(option.key)}\r\n disabled={resolving}\r\n className={`w-full flex items-center gap-3 px-4 py-3 rounded-[var(--radius-button)] border font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed ${getButtonClasses(option.variant)}`}\r\n >\r\n {resolving ? (\r\n <Loader2 className=\"w-5 h-5 animate-spin\" />\r\n ) : (\r\n <option.icon className=\"w-5 h-5\" />\r\n )}\r\n <span>{option.label}</span>\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"names":["withTenantContext","tenantSlug","url","config","apiClient","res","data","createEntraApi","apiContext","api","request","options","page","pageSize","sessionId","groupId","roleId","userId","conflictId","entraApi","normalizeValue","value","formatValue","isPhone","formatPhoneNumber","ConflictDiffView","entraData","localData","useTranslation","fieldLabels","phoneFields","fields","key","entraValue","localValue","isDifferent","f","jsxs","jsx","Cloud","Users","field","AlertCircle","useLocale","i18n","locale","useMemo","formatDate","date","defaultOptions","formatNumber","formatCurrency","currency","getResourceIcon","resourceType","Building2","Link2","getConflictTypeConfig","conflictType","ConflictCard","conflict","onResolve","className","t","ResourceIcon","typeConfig","Mail","AlertTriangle","Calendar","CheckCircle","NOTES_MAX_LENGTH","getButtonClasses","variant","ResolutionModal","onClose","resolving","notes","setNotes","useState","resolutionOptions","Merge","SkipForward","handleKeyDown","useCallback","e","useEffect","handleResolve","resolution","handleNotesChange","X","option","Loader2"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ResolutionModal-Duat18qV.js","sources":["../../src/services/api/entraApi.ts","../../src/components/platform/administration/ConflictDiffView.tsx","../../src/hooks/useLocale.ts","../../src/components/platform/administration/entra/ConflictCard.tsx","../../src/components/platform/administration/entra/ResolutionModal.tsx"],"sourcesContent":["import { api, apiClient } from './apiClient';\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) => ({\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 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\n// Types for Entra Dashboard/Sync Status\r\nexport interface EntraSyncStatusDto {\r\n resourceType: string;\r\n status: string;\r\n lastFullSyncAt: string | null;\r\n lastDeltaSyncAt: string | null;\r\n totalItemsSynced: number;\r\n pendingConflicts: number;\r\n lastError: string | null;\r\n}\r\n\r\nexport interface EntraSyncResultDto {\r\n syncSessionId: string;\r\n resourceType: string;\r\n success: boolean;\r\n errorMessage: string | null;\r\n startedAt: string;\r\n completedAt: string;\r\n totalProcessed: number;\r\n created: number;\r\n updated: number;\r\n deleted: number;\r\n skipped: number;\r\n conflicts: number;\r\n pendingConflicts: EntraSyncConflictDto[];\r\n}\r\n\r\nexport interface EntraSyncOptionsDto {\r\n useDeltaSync?: boolean;\r\n syncUsers?: boolean;\r\n syncGroups?: boolean;\r\n syncMemberships?: boolean;\r\n conflictPriority?: 'ManualReview' | 'EntraWins' | 'LocalWins';\r\n autoCreateReferences?: boolean;\r\n}\r\n\r\n// Types for Entra Sync History\r\nexport interface EntraSyncSessionDto {\r\n id: string;\r\n resourceType: string;\r\n isFullSync: boolean;\r\n status: string;\r\n startedAt: string;\r\n completedAt: string | null;\r\n errorMessage: string | null;\r\n totalProcessed: number;\r\n created: number;\r\n updated: number;\r\n deleted: number;\r\n skipped: number;\r\n conflicts: number;\r\n triggerType: string;\r\n triggeredByUserId: string | null;\r\n triggeredByUserName: string | null;\r\n duration: string | null;\r\n}\r\n\r\nexport interface EntraSyncHistoryResponse {\r\n sessions: EntraSyncSessionDto[];\r\n totalCount: number;\r\n page: number;\r\n pageSize: number;\r\n}\r\n\r\n// Types for Entra Conflicts\r\nexport interface ConflictDataDto {\r\n firstName: string | null;\r\n lastName: string | null;\r\n displayName: string | null;\r\n email: string | null;\r\n phoneNumber: string | null;\r\n mobilePhone: string | null;\r\n department: string | null;\r\n jobTitle: string | null;\r\n}\r\n\r\nexport interface EntraSyncConflictDto {\r\n id: string;\r\n resourceType: string;\r\n entraObjectId: string;\r\n entraDisplayName: string;\r\n entraEmail: string | null;\r\n existingLocalEntityId: string | null;\r\n existingLocalDisplayName: string | null;\r\n conflictType: string;\r\n conflictDescription: string;\r\n resolution: string;\r\n createdAt: string;\r\n entraData: ConflictDataDto | null;\r\n localData: ConflictDataDto | null;\r\n}\r\n\r\nexport interface ResolveConflictRequest {\r\n resolution: 'EntraPriority' | 'LocalPriority' | 'SkipImport' | 'MergeToExisting' | 'CreateNew';\r\n notes?: string;\r\n}\r\n\r\n// Types for Entra Groups\r\nexport interface EntraGroupDto {\r\n id: string;\r\n entraObjectId: string;\r\n displayName: string;\r\n description: string | null;\r\n mail: string | null;\r\n isSecurityGroup: boolean;\r\n isActive: boolean;\r\n lastSyncedAt: string;\r\n memberCount: number;\r\n assignedRoles: EntraGroupRoleDto[];\r\n /** Roles inherited from parent groups in the hierarchy */\r\n inheritedRoles: InheritedRoleDto[];\r\n // Parent-child relationship for nested groups\r\n parentGroupId: string | null;\r\n parentGroupName: string | null;\r\n childGroupCount: number;\r\n childGroups: EntraGroupSummaryDto[];\r\n}\r\n\r\nexport interface EntraGroupSummaryDto {\r\n id: string;\r\n entraObjectId: string;\r\n displayName: string;\r\n isSecurityGroup: boolean;\r\n memberCount: number;\r\n childGroupCount: number;\r\n}\r\n\r\nexport interface EntraGroupRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n assignedAt: string;\r\n assignedBy: string;\r\n /** If true, this role is inherited by child groups in the hierarchy */\r\n isInheritable: boolean;\r\n}\r\n\r\n/**\r\n * Represents a role inherited from a parent group in the hierarchy\r\n */\r\nexport interface InheritedRoleDto {\r\n roleId: string;\r\n roleName: string;\r\n assignedAt: string;\r\n assignedBy: string;\r\n /** The source group from which this role is inherited */\r\n sourceGroupId: string;\r\n sourceGroupName: string;\r\n /** How many levels up in the hierarchy (1 = direct parent) */\r\n inheritanceLevel: number;\r\n}\r\n\r\nexport interface EntraGroupMemberDto {\r\n userId: string;\r\n fullName: string;\r\n email: string;\r\n entraObjectId: string | null;\r\n isActive: boolean;\r\n addedAt: string;\r\n /** True if the member is directly in this group, false if via a child group */\r\n isDirect: boolean;\r\n /** Source group ID if member comes from a child group (null if direct) */\r\n sourceGroupId: string | null;\r\n /** Source group name if member comes from a child group */\r\n sourceGroupName: string | null;\r\n}\r\n\r\nexport interface AssignGroupToRoleRequest {\r\n roleId: string;\r\n /** If true, this role will be inherited by child groups. Default: true */\r\n isInheritable?: boolean;\r\n}\r\n\r\n// Types for Connection Test\r\nexport interface TestConnectionRequest {\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface TestConnectionResult {\r\n success: boolean;\r\n errorMessage: string | null;\r\n tenantName: string | null;\r\n userCount: number | null;\r\n groupCount: number | null;\r\n}\r\n\r\n// Types for Configuration\r\nexport interface EntraConfigurationDto {\r\n tenantId: string | null;\r\n clientId: string | null;\r\n hasClientSecret: boolean;\r\n syncEnabled: boolean;\r\n syncIntervalMinutes: number;\r\n useDeltaSync: boolean;\r\n defaultConflictResolution: string;\r\n autoCreateReferences: boolean;\r\n isConfigured: boolean;\r\n // User sync filters\r\n syncOnlyActiveUsers: boolean;\r\n /** Comma-separated list of user types to sync: member, guest, shared, room, equipment */\r\n syncUserTypes: string;\r\n}\r\n\r\nexport interface SaveEntraCredentialsRequest {\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface SaveEntraSyncSettingsRequest {\r\n syncEnabled: boolean;\r\n syncIntervalMinutes: number;\r\n useDeltaSync: boolean;\r\n defaultConflictResolution: string;\r\n autoCreateReferences: boolean;\r\n // User sync filters\r\n syncOnlyActiveUsers?: boolean;\r\n /** Comma-separated list of user types to sync: member, guest, shared, room, equipment */\r\n syncUserTypes?: string;\r\n}\r\n\r\n/**\r\n * Creates the Entra 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 createEntraApi = (tenantSlug?: string) => {\r\n const apiContext = tenantSlug ? withTenantContext(tenantSlug) : api;\r\n\r\n return {\r\n // Settings / Configuration\r\n settings: {\r\n /**\r\n * Get current Entra configuration\r\n */\r\n getConfiguration: () =>\r\n apiContext.get<EntraConfigurationDto>('/api/administration/entra/configuration'),\r\n\r\n /**\r\n * Test connection to Microsoft Entra ID with provided credentials\r\n */\r\n testConnection: (request: TestConnectionRequest) =>\r\n apiContext.post<TestConnectionResult>('/api/administration/entra/test-connection', request),\r\n\r\n /**\r\n * Test connection using stored credentials from database\r\n */\r\n testStoredConnection: () =>\r\n apiContext.post<TestConnectionResult>('/api/administration/entra/test-stored-connection'),\r\n\r\n /**\r\n * Save Entra credentials (TenantId, ClientId, ClientSecret)\r\n */\r\n saveCredentials: (request: SaveEntraCredentialsRequest) =>\r\n apiContext.put<{ message: string }>('/api/administration/entra/configuration/credentials', request),\r\n\r\n /**\r\n * Save Entra sync settings\r\n */\r\n saveSyncSettings: (request: SaveEntraSyncSettingsRequest) =>\r\n apiContext.put<{ message: string }>('/api/administration/entra/configuration/sync-settings', request),\r\n },\r\n\r\n // Dashboard / Sync Status\r\n dashboard: {\r\n /**\r\n * Get sync status for all resource types (Users, Groups, Memberships)\r\n */\r\n getSyncStatus: () =>\r\n apiContext.get<EntraSyncStatusDto[]>('/api/administration/entra/sync/status'),\r\n },\r\n\r\n // Sync Operations\r\n sync: {\r\n /**\r\n * Trigger full synchronization of all resources\r\n */\r\n syncAll: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync', options),\r\n\r\n /**\r\n * Trigger users synchronization only\r\n */\r\n syncUsers: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/users', options),\r\n\r\n /**\r\n * Trigger groups synchronization only\r\n */\r\n syncGroups: (options?: EntraSyncOptionsDto) =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/groups', options),\r\n\r\n /**\r\n * Trigger memberships synchronization only\r\n */\r\n syncMemberships: () =>\r\n apiContext.post<EntraSyncResultDto>('/api/administration/entra/sync/memberships'),\r\n\r\n /**\r\n * Force full sync (ignores delta tokens, re-syncs everything)\r\n */\r\n forceFullSync: () =>\r\n apiContext.post<{ message: string }>('/api/administration/entra/sync/force-full'),\r\n\r\n /**\r\n * Get sync history with pagination\r\n */\r\n getHistory: (page: number = 1, pageSize: number = 10) =>\r\n apiContext.get<EntraSyncHistoryResponse>(`/api/administration/entra/sync/history?page=${page}&pageSize=${pageSize}`),\r\n\r\n /**\r\n * Cancel a running sync session\r\n */\r\n cancelSession: (sessionId: string) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/sync/session/${sessionId}/cancel`),\r\n },\r\n\r\n // Groups Management\r\n groups: {\r\n /**\r\n * Get all Entra groups\r\n */\r\n getAll: () =>\r\n apiContext.get<EntraGroupDto[]>('/api/administration/entra/groups'),\r\n\r\n /**\r\n * Get a specific Entra group by ID\r\n */\r\n getById: (groupId: string) =>\r\n apiContext.get<EntraGroupDto>(`/api/administration/entra/groups/${groupId}`),\r\n\r\n /**\r\n * Assign a role to an Entra group\r\n */\r\n assignRole: (groupId: string, request: AssignGroupToRoleRequest) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/groups/${groupId}/roles`, request),\r\n\r\n /**\r\n * Remove a role from an Entra group\r\n */\r\n removeRole: (groupId: string, roleId: string) =>\r\n apiContext.delete<{ message: string }>(`/api/administration/entra/groups/${groupId}/roles/${roleId}`),\r\n\r\n /**\r\n * Get members of an Entra group\r\n */\r\n getMembers: (groupId: string) =>\r\n apiContext.get<EntraGroupMemberDto[]>(`/api/administration/entra/groups/${groupId}/members`),\r\n },\r\n\r\n // Users Management (Entra-related)\r\n users: {\r\n /**\r\n * Get Entra groups for a specific user\r\n */\r\n getGroups: (userId: string) =>\r\n apiContext.get<EntraGroupDto[]>(`/api/administration/entra/users/${userId}/groups`),\r\n },\r\n\r\n // Conflicts Management\r\n conflicts: {\r\n /**\r\n * Get all pending sync conflicts\r\n */\r\n getAll: () =>\r\n apiContext.get<EntraSyncConflictDto[]>('/api/administration/entra/conflicts'),\r\n\r\n /**\r\n * Resolve a specific conflict\r\n */\r\n resolve: (conflictId: string, data: ResolveConflictRequest) =>\r\n apiContext.post<{ message: string }>(`/api/administration/entra/conflicts/${conflictId}/resolve`, data),\r\n },\r\n };\r\n};\r\n\r\n// Default export uses localStorage tenant slug (backward compatible)\r\nexport const entraApi = createEntraApi();\r\n\r\n// Export factory for explicit tenant context\r\nexport { createEntraApi };\r\n","import type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport { Cloud, Users, AlertCircle } from 'lucide-react';\r\nimport type { ConflictDataDto } from '@/services/api/entraApi';\r\nimport { formatPhoneNumber } from '@/utils/formatPhone';\r\n\r\ninterface ConflictDiffViewProps {\r\n readonly entraData: ConflictDataDto | null;\r\n readonly localData: ConflictDataDto | null;\r\n}\r\n\r\ninterface FieldDiff {\r\n key: string;\r\n label: string;\r\n entraValue: string | null;\r\n localValue: string | null;\r\n isDifferent: boolean;\r\n}\r\n\r\nconst normalizeValue = (value: string | null | undefined): string | null => {\r\n if (value === null || value === undefined || value === '') return null;\r\n return value.trim();\r\n};\r\n\r\nconst formatValue = (value: string | null, isPhone: boolean = false): string => {\r\n if (!value) return '-';\r\n if (isPhone) {\r\n const formatted = formatPhoneNumber(value, 'CH', 'international');\r\n return formatted || value;\r\n }\r\n return value;\r\n};\r\n\r\nexport function ConflictDiffView({ entraData, localData }: ConflictDiffViewProps): ReactElement | null {\r\n const { t } = useTranslation(['entra', 'common']);\r\n\r\n if (!entraData && !localData) {\r\n return null;\r\n }\r\n\r\n const fieldLabels: Record<string, string> = {\r\n firstName: t('entra:conflicts.fields.firstName', 'First Name'),\r\n lastName: t('entra:conflicts.fields.lastName', 'Last Name'),\r\n displayName: t('entra:conflicts.fields.displayName', 'Display Name'),\r\n email: t('entra:conflicts.fields.upn', 'UPN'),\r\n phoneNumber: t('entra:conflicts.fields.phoneNumber', 'Phone'),\r\n mobilePhone: t('entra:conflicts.fields.mobilePhone', 'Mobile'),\r\n department: t('entra:conflicts.fields.department', 'Department'),\r\n jobTitle: t('entra:conflicts.fields.jobTitle', 'Job Title'),\r\n };\r\n\r\n const phoneFields = ['phoneNumber', 'mobilePhone'];\r\n\r\n const fields: FieldDiff[] = Object.keys(fieldLabels).map((key) => {\r\n const entraValue = normalizeValue(entraData?.[key as keyof ConflictDataDto]);\r\n const localValue = normalizeValue(localData?.[key as keyof ConflictDataDto]);\r\n const isDifferent = entraValue !== localValue && (entraValue !== null || localValue !== null);\r\n\r\n return {\r\n key,\r\n label: fieldLabels[key],\r\n entraValue,\r\n localValue,\r\n isDifferent,\r\n };\r\n });\r\n\r\n const hasDifferences = fields.some((f) => f.isDifferent);\r\n\r\n if (!hasDifferences) {\r\n return (\r\n <div className=\"mt-4 pt-4 border-t border-[var(--border-color)]\">\r\n <p className=\"text-sm text-[var(--text-secondary)] italic\">\r\n {t('entra:conflicts.noDifferences', 'No field differences detected')}\r\n </p>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"mt-4 pt-4 border-t border-[var(--border-color)]\">\r\n <div className=\"overflow-hidden rounded-[var(--radius-card)] border border-[var(--border-color)]\">\r\n <table className=\"w-full text-sm\">\r\n <thead>\r\n <tr className=\"bg-[var(--bg-secondary)]\">\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--text-secondary)] w-1/4\">\r\n {t('entra:conflicts.field', 'Field')}\r\n </th>\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--color-accent-600)] w-[37.5%]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Cloud className=\"w-4 h-4\" />\r\n {t('entra:conflicts.entraData', 'Entra')}\r\n </div>\r\n </th>\r\n <th className=\"px-4 py-2 text-left font-medium text-[var(--success-text)] w-[37.5%]\">\r\n <div className=\"flex items-center gap-2\">\r\n <Users className=\"w-4 h-4\" />\r\n {t('entra:conflicts.localData', 'Local')}\r\n </div>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody className=\"divide-y divide-[var(--border-color)]\">\r\n {fields\r\n .filter((f) => f.isDifferent)\r\n .map((field) => (\r\n <tr\r\n key={field.key}\r\n className=\"bg-[var(--bg-card)] hover:bg-[var(--bg-secondary)] transition-colors\"\r\n >\r\n <td className=\"px-4 py-3 font-medium text-[var(--text-primary)]\">\r\n <div className=\"flex items-center gap-2\">\r\n <AlertCircle className=\"w-4 h-4 text-[var(--color-accent-500)]\" />\r\n {field.label}\r\n </div>\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n {field.entraValue ? (\r\n <span className=\"text-[var(--color-accent-700)] bg-[var(--color-accent-500)]/10 px-2 py-1 rounded\">\r\n {formatValue(field.entraValue, phoneFields.includes(field.key))}\r\n </span>\r\n ) : (\r\n <span className=\"text-[var(--text-tertiary)] italic\">\r\n {formatValue(field.entraValue, phoneFields.includes(field.key))}\r\n </span>\r\n )}\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n {field.localValue ? (\r\n <span className=\"text-[var(--success-text)] bg-[var(--success-bg)] px-2 py-1 rounded\">\r\n {formatValue(field.localValue, phoneFields.includes(field.key))}\r\n </span>\r\n ) : (\r\n <span className=\"text-[var(--text-tertiary)] italic\">\r\n {formatValue(field.localValue, phoneFields.includes(field.key))}\r\n </span>\r\n )}\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n <p className=\"mt-2 text-xs text-[var(--text-tertiary)]\">\r\n {t('entra:conflicts.diffCount', '{{count}} field(s) with differences', {\r\n count: fields.filter((f) => f.isDifferent).length,\r\n })}\r\n </p>\r\n </div>\r\n );\r\n}\r\n","import { useMemo } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\n\r\n/**\r\n * Hook for locale-aware formatting utilities.\r\n * Automatically uses the current i18n language for formatting.\r\n */\r\nexport function useLocale(): {\n locale: string;\n formatDate: (date: string | Date, options?: Intl.DateTimeFormatOptions) => string;\n formatNumber: (value: number, options?: Intl.NumberFormatOptions) => string;\n formatCurrency: (value: number, currency?: string) => string;\n} {\r\n const { i18n } = useTranslation();\r\n\r\n const locale = useMemo(() => {\r\n // Map i18n language codes to Intl locale codes\r\n const localeMap: Record<string, string> = {\r\n fr: 'fr-FR',\r\n en: 'en-US',\r\n de: 'de-DE',\r\n it: 'it-IT',\r\n };\r\n return localeMap[i18n.language] || 'en-US';\r\n }, [i18n.language]);\r\n\r\n const formatDate = useMemo(() => {\r\n return (date: string | Date, options?: Intl.DateTimeFormatOptions) => {\r\n const defaultOptions: Intl.DateTimeFormatOptions = {\r\n dateStyle: 'medium',\r\n timeStyle: 'short',\r\n };\r\n return new Intl.DateTimeFormat(locale, options || defaultOptions).format(\r\n typeof date === 'string' ? new Date(date) : date\r\n );\r\n };\r\n }, [locale]);\r\n\r\n const formatNumber = useMemo(() => {\r\n return (value: number, options?: Intl.NumberFormatOptions) => {\r\n return new Intl.NumberFormat(locale, options).format(value);\r\n };\r\n }, [locale]);\r\n\r\n const formatCurrency = useMemo(() => {\r\n return (value: number, currency = 'EUR') => {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n }).format(value);\r\n };\r\n }, [locale]);\r\n\r\n return {\r\n locale,\r\n formatDate,\r\n formatNumber,\r\n formatCurrency,\r\n };\r\n}\r\n","import type { ReactElement } from 'react';\nimport { useTranslation } from 'react-i18next';\r\nimport {\r\n AlertTriangle,\r\n CheckCircle,\r\n Users,\r\n Building2,\r\n Link2,\r\n Cloud,\r\n Calendar,\r\n Mail,\r\n} from 'lucide-react';\r\nimport type { EntraSyncConflictDto } from '@/services/api/entraApi';\r\nimport { ConflictDiffView } from '@/components/platform/administration/ConflictDiffView';\r\nimport { useLocale } from '@/hooks/useLocale';\r\n\r\nexport interface ConflictCardProps {\r\n readonly conflict: EntraSyncConflictDto;\r\n readonly onResolve: (conflict: EntraSyncConflictDto) => void;\r\n readonly className?: string;\r\n}\r\n\r\nconst getResourceIcon = (resourceType: string) => {\r\n switch (resourceType) {\r\n case 'User':\r\n return Users;\r\n case 'Group':\r\n return Building2;\r\n case 'Membership':\r\n return Link2;\r\n default:\r\n return Cloud;\r\n }\r\n};\r\n\r\nconst getConflictTypeConfig = (conflictType: string) => {\r\n switch (conflictType) {\r\n case 'Duplicate':\r\n return {\r\n bg: 'bg-[var(--error-bg)]',\r\n text: 'text-[var(--error-text)]',\r\n border: 'border-[var(--error-border)]',\r\n };\r\n default:\r\n return {\r\n bg: 'bg-[var(--color-accent-500)]/10',\r\n text: 'text-[var(--color-accent-700)]',\r\n border: 'border-[var(--color-accent-300)]',\r\n };\r\n }\r\n};\r\n\r\nexport function ConflictCard({ conflict, onResolve, className = '' }: ConflictCardProps): ReactElement {\r\n const { t } = useTranslation(['entra']);\r\n const { formatDate } = useLocale();\r\n\r\n const ResourceIcon = getResourceIcon(conflict.resourceType);\r\n const typeConfig = getConflictTypeConfig(conflict.conflictType);\r\n\r\n return (\r\n <div className={`h-full flex flex-col rounded-[var(--radius-card)] border border-[var(--border-color)] bg-[var(--bg-card)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`}>\r\n {/* Header */}\r\n <div className=\"px-4 py-3 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-500)]/20\">\r\n <ResourceIcon className=\"w-5 h-5 text-[var(--color-accent-600)]\" />\r\n </div>\r\n <div>\r\n <h3 className=\"font-semibold text-[var(--text-primary)]\">\r\n {conflict.entraDisplayName}\r\n </h3>\r\n {conflict.entraEmail && (\r\n <div className=\"flex items-center gap-1 text-sm text-[var(--text-secondary)]\">\r\n <Mail className=\"w-3.5 h-3.5\" />\r\n {conflict.entraEmail}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${typeConfig.bg} ${typeConfig.text} border ${typeConfig.border}`}>\r\n <AlertTriangle className=\"w-3.5 h-3.5\" />\r\n {t(`entra:conflicts.types.${conflict.conflictType}`)}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n {/* Content */}\r\n <div className=\"flex-1 flex flex-col p-4 space-y-4\">\r\n <p className=\"text-sm text-[var(--text-secondary)]\">\r\n {conflict.conflictDescription}\r\n </p>\r\n\r\n <ConflictDiffView entraData={conflict.entraData} localData={conflict.localData} />\r\n\r\n {/* Footer */}\r\n <div className=\"mt-auto flex items-center justify-between pt-4 border-t border-[var(--border-color)]\">\r\n <div className=\"flex items-center gap-1.5 text-xs text-[var(--text-tertiary)]\">\r\n <Calendar className=\"w-3.5 h-3.5\" />\r\n {t('entra:conflicts.createdAt', { date: formatDate(conflict.createdAt) })}\r\n </div>\r\n <button\r\n onClick={() => onResolve(conflict)}\r\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-[var(--radius-button)] bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] transition-colors shadow-sm\"\r\n >\r\n <CheckCircle className=\"w-4 h-4\" />\r\n {t('entra:conflicts.resolve')}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useEffect, useCallback, useState } from 'react';\r\nimport type { ReactElement } from 'react';\r\nimport { useTranslation } from 'react-i18next';\r\nimport {\r\n AlertTriangle,\r\n X,\r\n Loader2,\r\n Cloud,\r\n Users,\r\n Merge,\r\n SkipForward,\r\n} from 'lucide-react';\r\nimport type { EntraSyncConflictDto, ResolveConflictRequest } from '@/services/api/entraApi';\r\n\r\nconst NOTES_MAX_LENGTH = 500;\r\n\r\nexport interface ResolutionModalProps {\r\n readonly conflict: EntraSyncConflictDto;\r\n readonly onClose: () => void;\r\n readonly onResolve: (resolution: ResolveConflictRequest['resolution'], notes?: string) => Promise<void>;\r\n readonly resolving?: boolean;\r\n}\r\n\r\nconst getButtonClasses = (variant: string) => {\r\n switch (variant) {\r\n case 'primary':\r\n return 'bg-[var(--color-accent-600)] text-white hover:bg-[var(--color-accent-700)] border-transparent';\r\n case 'success':\r\n return 'bg-[var(--success-bg)] text-[var(--success-text)] hover:opacity-90 border-[var(--success-border)]';\r\n case 'secondary':\r\n return 'bg-[var(--bg-secondary)] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] border-[var(--border-color)]';\r\n case 'ghost':\r\n return 'bg-transparent text-[var(--text-secondary)] hover:bg-[var(--bg-secondary)] border-[var(--border-color)]';\r\n default:\r\n return 'bg-[var(--bg-secondary)] text-[var(--text-primary)] border-[var(--border-color)]';\r\n }\r\n};\r\n\r\nexport function ResolutionModal({\r\n conflict,\r\n onClose,\r\n onResolve,\r\n resolving = false,\r\n}: ResolutionModalProps): ReactElement {\r\n const { t } = useTranslation(['entra']);\r\n const [notes, setNotes] = useState('');\r\n\r\n const resolutionOptions = [\r\n {\r\n key: 'EntraPriority' as const,\r\n icon: Cloud,\r\n label: t('entra:conflicts.resolutions.entraWins'),\r\n variant: 'primary' as const,\r\n },\r\n {\r\n key: 'LocalPriority' as const,\r\n icon: Users,\r\n label: t('entra:conflicts.resolutions.localWins'),\r\n variant: 'success' as const,\r\n },\r\n {\r\n key: 'MergeToExisting' as const,\r\n icon: Merge,\r\n label: t('entra:conflicts.resolutions.merge'),\r\n variant: 'secondary' as const,\r\n },\r\n {\r\n key: 'SkipImport' as const,\r\n icon: SkipForward,\r\n label: t('entra:conflicts.resolutions.skip'),\r\n variant: 'ghost' as const,\r\n },\r\n ];\r\n\r\n // Handle escape key to close modal\r\n const handleKeyDown = useCallback((e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && !resolving) {\r\n onClose();\r\n }\r\n }, [onClose, resolving]);\r\n\r\n useEffect(() => {\r\n document.addEventListener('keydown', handleKeyDown);\r\n // Lock body scroll\r\n document.body.style.overflow = 'hidden';\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown);\r\n document.body.style.overflow = '';\r\n };\r\n }, [handleKeyDown]);\r\n\r\n const handleResolve = async (resolution: ResolveConflictRequest['resolution']) => {\r\n await onResolve(resolution, notes.trim() || undefined);\r\n };\r\n\r\n const handleNotesChange = (value: string) => {\r\n // Limit notes length\r\n if (value.length <= NOTES_MAX_LENGTH) {\r\n setNotes(value);\r\n }\r\n };\r\n\r\n return (\r\n <div\r\n className=\"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm\"\r\n onClick={(e) => {\r\n if (e.target === e.currentTarget && !resolving) {\r\n onClose();\r\n }\r\n }}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Escape' && !resolving) {\r\n onClose();\r\n }\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label=\"Resolve conflict\"\r\n >\r\n <div className=\"w-full max-w-lg rounded-[var(--radius-card)] bg-[var(--bg-card)] shadow-2xl overflow-hidden\">\r\n {/* Header */}\r\n <div className=\"px-6 py-4 bg-gradient-to-r from-[var(--color-accent-500)]/10 to-[var(--color-accent-600)]/5 border-b border-[var(--border-color)]\">\r\n <div className=\"flex items-center justify-between\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-500)]/20\">\r\n <AlertTriangle className=\"w-5 h-5 text-[var(--color-accent-600)]\" />\r\n </div>\r\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">\r\n {t('entra:conflicts.resolveTitle')}\r\n </h2>\r\n </div>\r\n <button\r\n onClick={onClose}\r\n disabled={resolving}\r\n className=\"p-2 rounded-lg text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-secondary)] transition-colors disabled:opacity-50\"\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=\"p-6 space-y-4\">\r\n {/* Conflict Info */}\r\n <div className=\"p-4 rounded-lg bg-[var(--bg-secondary)] border border-[var(--border-color)]\">\r\n <p className=\"font-medium text-[var(--text-primary)]\">{conflict.entraDisplayName}</p>\r\n <p className=\"text-sm text-[var(--text-secondary)] mt-1\">{conflict.conflictDescription}</p>\r\n </div>\r\n\r\n {/* Notes */}\r\n <div>\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <label className=\"block text-sm font-medium text-[var(--text-primary)]\">\r\n {t('entra:conflicts.notes')}\r\n </label>\r\n <span className=\"text-xs text-[var(--text-tertiary)]\">\r\n {notes.length}/{NOTES_MAX_LENGTH}\r\n </span>\r\n </div>\r\n <textarea\r\n value={notes}\r\n onChange={(e) => handleNotesChange(e.target.value)}\r\n placeholder={t('entra:conflicts.notesPlaceholder')}\r\n maxLength={NOTES_MAX_LENGTH}\r\n disabled={resolving}\r\n className=\"w-full px-3 py-2.5 rounded-[var(--radius-input)] border border-[var(--border-color)] bg-[var(--bg-secondary)] text-[var(--text-primary)] placeholder-[var(--text-tertiary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-500)] focus:border-transparent transition-shadow resize-none disabled:opacity-50\"\r\n rows={3}\r\n />\r\n </div>\r\n\r\n {/* Resolution Options */}\r\n <div className=\"space-y-2\">\r\n {resolutionOptions.map((option) => (\r\n <button\r\n key={option.key}\r\n onClick={() => handleResolve(option.key)}\r\n disabled={resolving}\r\n className={`w-full flex items-center gap-3 px-4 py-3 rounded-[var(--radius-button)] border font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed ${getButtonClasses(option.variant)}`}\r\n >\r\n {resolving ? (\r\n <Loader2 className=\"w-5 h-5 animate-spin\" />\r\n ) : (\r\n <option.icon className=\"w-5 h-5\" />\r\n )}\r\n <span>{option.label}</span>\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n"],"names":["withTenantContext","tenantSlug","url","config","apiClient","res","data","createEntraApi","apiContext","api","request","options","page","pageSize","sessionId","groupId","roleId","userId","conflictId","entraApi","normalizeValue","value","formatValue","isPhone","formatPhoneNumber","ConflictDiffView","entraData","localData","useTranslation","fieldLabels","phoneFields","fields","key","entraValue","localValue","isDifferent","f","jsxs","jsx","Cloud","Users","field","AlertCircle","useLocale","i18n","locale","useMemo","formatDate","date","defaultOptions","formatNumber","formatCurrency","currency","getResourceIcon","resourceType","Building2","Link2","getConflictTypeConfig","conflictType","ConflictCard","conflict","onResolve","className","t","ResourceIcon","typeConfig","Mail","AlertTriangle","Calendar","CheckCircle","NOTES_MAX_LENGTH","getButtonClasses","variant","ResolutionModal","onClose","resolving","notes","setNotes","useState","resolutionOptions","Merge","SkipForward","handleKeyDown","useCallback","e","useEffect","handleResolve","resolution","handleNotesChange","X","option","Loader2"],"mappings":"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"}
|
|
@@ -2,9 +2,9 @@ import { jsx as e, jsxs as t, Fragment as er } from "react/jsx-runtime";
|
|
|
2
2
|
import Le, { useState as d, useMemo as te, useCallback as S, createRef as rr, useEffect as je } from "react";
|
|
3
3
|
import { useParams as tr, useNavigate as ar, Link as Ee } from "react-router-dom";
|
|
4
4
|
import { useTranslation as He } from "react-i18next";
|
|
5
|
-
import { o as sr, s as lr, x as nr, y as or, k as N, B as ir } from "./index-
|
|
5
|
+
import { o as sr, s as lr, x as nr, y as or, k as N, B as ir } from "./index-lpIzhufD.js";
|
|
6
6
|
import { Loader2 as g, AlertCircle as Ue, Check as Ge, X as Ne, Save as _e, Star as cr, Eye as $e, Ban as dr, ChevronRight as Ie, ChevronDown as Me, FolderOpen as mr, Database as ur, FileText as vr, Box as pr, Play as xr, UserPlus as br, Trash2 as we, Edit as hr, Plus as Ce, Shield as Pe, Settings as gr, Users as Oe, UsersRound as Te, Languages as fr, ArrowLeft as yr, Lock as Be, AlertTriangle as Nr, CheckCircle as wr, Search as ze, Edit2 as Cr } from "lucide-react";
|
|
7
|
-
import { g as Dr } from "./groupsApi-
|
|
7
|
+
import { g as Dr } from "./groupsApi-QzXI-5xu.js";
|
|
8
8
|
import { d as kr, c as Sr, g as Rr, a as Ar, P as Tr, b as Lr } from "./PermissionPopover-CR6SD8ch.js";
|
|
9
9
|
const $r = {
|
|
10
10
|
"*": /* @__PURE__ */ e(Pe, { className: "h-3 w-3" }),
|
|
@@ -1337,4 +1337,4 @@ function _r() {
|
|
|
1337
1337
|
export {
|
|
1338
1338
|
_r as RoleDetailPage
|
|
1339
1339
|
};
|
|
1340
|
-
//# sourceMappingURL=RoleDetailPage-
|
|
1340
|
+
//# sourceMappingURL=RoleDetailPage-BQffUSnt.js.map
|