@samanhappy/mcphub 1.1.14 → 1.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.fr.md +1 -1
  2. package/README.md +1 -1
  3. package/README.zh.md +1 -1
  4. package/dist/betterAuth.js +3 -3
  5. package/dist/betterAuth.js.map +1 -1
  6. package/dist/cli/commands/keys.js +22 -3
  7. package/dist/cli/commands/keys.js.map +1 -1
  8. package/dist/clients/mcpSdkClient.js +2667 -1448
  9. package/dist/clients/mcpSdkClient.js.map +4 -4
  10. package/dist/controllers/bearerKeyController.js +49 -3
  11. package/dist/controllers/bearerKeyController.js.map +1 -1
  12. package/dist/controllers/credentialBindingController.js +1 -1
  13. package/dist/controllers/credentialBindingController.js.map +1 -1
  14. package/dist/controllers/groupController.js +12 -5
  15. package/dist/controllers/groupController.js.map +1 -1
  16. package/dist/controllers/oauthRelayController.js +65 -0
  17. package/dist/controllers/oauthRelayController.js.map +1 -0
  18. package/dist/controllers/personalOAuthController.js +10 -1
  19. package/dist/controllers/personalOAuthController.js.map +1 -1
  20. package/dist/controllers/serverController.js +23 -14
  21. package/dist/controllers/serverController.js.map +1 -1
  22. package/dist/controllers/userController.js +11 -1
  23. package/dist/controllers/userController.js.map +1 -1
  24. package/dist/dao/BearerKeyDao.js +10 -5
  25. package/dist/dao/BearerKeyDao.js.map +1 -1
  26. package/dist/dao/BearerKeyDaoDbImpl.js +16 -5
  27. package/dist/dao/BearerKeyDaoDbImpl.js.map +1 -1
  28. package/dist/dao/GroupDaoDbImpl.js +11 -0
  29. package/dist/dao/GroupDaoDbImpl.js.map +1 -1
  30. package/dist/dao/ServerDao.js +5 -1
  31. package/dist/dao/ServerDao.js.map +1 -1
  32. package/dist/dao/ServerDaoDbImpl.js +3 -0
  33. package/dist/dao/ServerDaoDbImpl.js.map +1 -1
  34. package/dist/dao/UserDaoDbImpl.js +4 -0
  35. package/dist/dao/UserDaoDbImpl.js.map +1 -1
  36. package/dist/db/entities/BearerKey.js +8 -0
  37. package/dist/db/entities/BearerKey.js.map +1 -1
  38. package/dist/db/entities/Group.js +4 -0
  39. package/dist/db/entities/Group.js.map +1 -1
  40. package/dist/db/entities/Server.js +4 -0
  41. package/dist/db/entities/Server.js.map +1 -1
  42. package/dist/db/entities/User.js +4 -0
  43. package/dist/db/entities/User.js.map +1 -1
  44. package/dist/db/repositories/ServerRepository.js +1 -0
  45. package/dist/db/repositories/ServerRepository.js.map +1 -1
  46. package/dist/middlewares/auth.js +7 -1
  47. package/dist/middlewares/auth.js.map +1 -1
  48. package/dist/routes/index.js +2 -0
  49. package/dist/routes/index.js.map +1 -1
  50. package/dist/services/authorizationService.js.map +1 -1
  51. package/dist/services/capabilitySnapshotService.js +2 -1
  52. package/dist/services/capabilitySnapshotService.js.map +1 -1
  53. package/dist/services/credentialBindingService.js +9 -1
  54. package/dist/services/credentialBindingService.js.map +1 -1
  55. package/dist/services/dataService.js +4 -2
  56. package/dist/services/dataService.js.map +1 -1
  57. package/dist/services/groupService.js +15 -7
  58. package/dist/services/groupService.js.map +1 -1
  59. package/dist/services/mcpEventBus.js +6 -0
  60. package/dist/services/mcpEventBus.js.map +1 -0
  61. package/dist/services/mcpService.js +26 -2
  62. package/dist/services/mcpService.js.map +1 -1
  63. package/dist/services/personalOAuthService.js +2 -0
  64. package/dist/services/personalOAuthService.js.map +1 -1
  65. package/dist/services/requestContextService.js +15 -0
  66. package/dist/services/requestContextService.js.map +1 -1
  67. package/dist/services/serverConfigEnvironment.js +2 -1
  68. package/dist/services/serverConfigEnvironment.js.map +1 -1
  69. package/dist/services/sseService.js +58 -6
  70. package/dist/services/sseService.js.map +1 -1
  71. package/dist/services/templateService.js +11 -5
  72. package/dist/services/templateService.js.map +1 -1
  73. package/dist/services/userService.js +5 -1
  74. package/dist/services/userService.js.map +1 -1
  75. package/dist/utils/groupAccess.js +6 -4
  76. package/dist/utils/groupAccess.js.map +1 -1
  77. package/dist/utils/groupKeyScope.js +11 -5
  78. package/dist/utils/groupKeyScope.js.map +1 -1
  79. package/dist/utils/migration.js +5 -0
  80. package/dist/utils/migration.js.map +1 -1
  81. package/dist/utils/oauthBearer.js +1 -0
  82. package/dist/utils/oauthBearer.js.map +1 -1
  83. package/dist/utils/oauthServerMetadata.js +21 -0
  84. package/dist/utils/oauthServerMetadata.js.map +1 -1
  85. package/dist/utils/serverConfigPersistence.js +16 -1
  86. package/dist/utils/serverConfigPersistence.js.map +1 -1
  87. package/dist/utils/serverConfigValidation.js +1 -0
  88. package/dist/utils/serverConfigValidation.js.map +1 -1
  89. package/dist/utils/settingsSync.js +3 -0
  90. package/dist/utils/settingsSync.js.map +1 -1
  91. package/frontend/dist/assets/{ActivityPage-C8Zj4FeE.js → ActivityPage-Bi-TjKez.js} +2 -2
  92. package/frontend/dist/assets/{ActivityPage-C8Zj4FeE.js.map → ActivityPage-Bi-TjKez.js.map} +1 -1
  93. package/frontend/dist/assets/{ConfirmDialog-BYdfb115.js → ConfirmDialog-BJpzQmMw.js} +2 -2
  94. package/frontend/dist/assets/{ConfirmDialog-BYdfb115.js.map → ConfirmDialog-BJpzQmMw.js.map} +1 -1
  95. package/frontend/dist/assets/CredentialsPage-C4Ucer3d.js +2 -0
  96. package/frontend/dist/assets/CredentialsPage-C4Ucer3d.js.map +1 -0
  97. package/frontend/dist/assets/{Dashboard-CCiZcGi4.js → Dashboard-C_2KkuoD.js} +2 -2
  98. package/frontend/dist/assets/{Dashboard-CCiZcGi4.js.map → Dashboard-C_2KkuoD.js.map} +1 -1
  99. package/frontend/dist/assets/{DeleteDialog-BaCsEkJA.js → DeleteDialog-BoMYLx3J.js} +2 -2
  100. package/frontend/dist/assets/{DeleteDialog-BaCsEkJA.js.map → DeleteDialog-BoMYLx3J.js.map} +1 -1
  101. package/frontend/dist/assets/{EndpointCopy-CmKx4RVL.js → EndpointCopy-DITVC2YQ.js} +2 -2
  102. package/frontend/dist/assets/{EndpointCopy-CmKx4RVL.js.map → EndpointCopy-DITVC2YQ.js.map} +1 -1
  103. package/frontend/dist/assets/GroupsPage-sE6YJyuK.js +33 -0
  104. package/frontend/dist/assets/GroupsPage-sE6YJyuK.js.map +1 -0
  105. package/frontend/dist/assets/{LoginPage-Hn1uu0vB.js → LoginPage-CFsd_nY-.js} +2 -2
  106. package/frontend/dist/assets/{LoginPage-Hn1uu0vB.js.map → LoginPage-CFsd_nY-.js.map} +1 -1
  107. package/frontend/dist/assets/{LogsPage-JMCtTjCv.js → LogsPage-BxZuGDuc.js} +2 -2
  108. package/frontend/dist/assets/{LogsPage-JMCtTjCv.js.map → LogsPage-BxZuGDuc.js.map} +1 -1
  109. package/frontend/dist/assets/MarketPage-CDBs4vMb.js +2 -0
  110. package/frontend/dist/assets/{MarketPage-Bvi1KESo.js.map → MarketPage-CDBs4vMb.js.map} +1 -1
  111. package/frontend/dist/assets/{OAuthConsentPage-DofQEZAU.js → OAuthConsentPage-BN_ucViM.js} +2 -2
  112. package/frontend/dist/assets/{OAuthConsentPage-DofQEZAU.js.map → OAuthConsentPage-BN_ucViM.js.map} +1 -1
  113. package/frontend/dist/assets/{Pagination-D99WHhBx.js → Pagination-S-7lzy59.js} +2 -2
  114. package/frontend/dist/assets/{Pagination-D99WHhBx.js.map → Pagination-S-7lzy59.js.map} +1 -1
  115. package/frontend/dist/assets/{PromptsPage-B-8vczAR.js → PromptsPage-Bl3ySXGK.js} +2 -2
  116. package/frontend/dist/assets/{PromptsPage-B-8vczAR.js.map → PromptsPage-Bl3ySXGK.js.map} +1 -1
  117. package/frontend/dist/assets/{ResourcesPage-DLAIatoG.js → ResourcesPage-BOzQkQ92.js} +2 -2
  118. package/frontend/dist/assets/{ResourcesPage-DLAIatoG.js.map → ResourcesPage-BOzQkQ92.js.map} +1 -1
  119. package/frontend/dist/assets/{ServersPage-wVLsTL_f.js → ServersPage-DHYwQ5AM.js} +5 -5
  120. package/frontend/dist/assets/{ServersPage-wVLsTL_f.js.map → ServersPage-DHYwQ5AM.js.map} +1 -1
  121. package/frontend/dist/assets/SettingsPage-CSJ35jlM.js +14 -0
  122. package/frontend/dist/assets/SettingsPage-CSJ35jlM.js.map +1 -0
  123. package/frontend/dist/assets/{StatusDot-00-sTQKv.js → StatusDot-NSjojGCy.js} +2 -2
  124. package/frontend/dist/assets/{StatusDot-00-sTQKv.js.map → StatusDot-NSjojGCy.js.map} +1 -1
  125. package/frontend/dist/assets/{ToggleGroup-CtWBng3r.js → ToggleGroup-BIZWpNzm.js} +2 -2
  126. package/frontend/dist/assets/{ToggleGroup-CtWBng3r.js.map → ToggleGroup-BIZWpNzm.js.map} +1 -1
  127. package/frontend/dist/assets/UserGroupSelection-CN0weGXJ.js +2 -0
  128. package/frontend/dist/assets/UserGroupSelection-CN0weGXJ.js.map +1 -0
  129. package/frontend/dist/assets/UsersPage-DC51v2b8.js +2 -0
  130. package/frontend/dist/assets/UsersPage-DC51v2b8.js.map +1 -0
  131. package/frontend/dist/assets/{clipboard-2RRFM-q8.js → clipboard-ik5YV95M.js} +2 -2
  132. package/frontend/dist/assets/{clipboard-2RRFM-q8.js.map → clipboard-ik5YV95M.js.map} +1 -1
  133. package/frontend/dist/assets/{costRevision-Ntl7VtWo.js → costRevision-Bxov9_vH.js} +2 -2
  134. package/frontend/dist/assets/{costRevision-Ntl7VtWo.js.map → costRevision-Bxov9_vH.js.map} +1 -1
  135. package/frontend/dist/assets/framework-vendor-D0S_ys6r.js +61 -0
  136. package/frontend/dist/assets/framework-vendor-D0S_ys6r.js.map +1 -0
  137. package/frontend/dist/assets/{i18n-vendor-DVmjrzgW.js → i18n-vendor-COTriSc6.js} +2 -2
  138. package/frontend/dist/assets/{i18n-vendor-DVmjrzgW.js.map → i18n-vendor-COTriSc6.js.map} +1 -1
  139. package/frontend/dist/assets/{icons-vendor-CyAS6fj0.js → icons-vendor-DZjjGrOu.js} +2 -2
  140. package/frontend/dist/assets/{icons-vendor-CyAS6fj0.js.map → icons-vendor-DZjjGrOu.js.map} +1 -1
  141. package/frontend/dist/assets/index-6AyPtx0A.js +3 -0
  142. package/frontend/dist/assets/index-6AyPtx0A.js.map +1 -0
  143. package/frontend/dist/assets/{resourceService-BB4UM6IQ.js → resourceService-DMzCAZ0B.js} +2 -2
  144. package/frontend/dist/assets/{resourceService-BB4UM6IQ.js.map → resourceService-DMzCAZ0B.js.map} +1 -1
  145. package/frontend/dist/assets/{useCostData-C3XnQyUj.js → useCostData-CcFSYViI.js} +2 -2
  146. package/frontend/dist/assets/{useCostData-C3XnQyUj.js.map → useCostData-CcFSYViI.js.map} +1 -1
  147. package/frontend/dist/assets/variableDetection-CbYSa1ka.js +80 -0
  148. package/frontend/dist/assets/variableDetection-CbYSa1ka.js.map +1 -0
  149. package/frontend/dist/index.html +4 -4
  150. package/package.json +2 -2
  151. package/frontend/dist/assets/CredentialsPage-BnzqPYjg.js +0 -2
  152. package/frontend/dist/assets/CredentialsPage-BnzqPYjg.js.map +0 -1
  153. package/frontend/dist/assets/GroupsPage-DHgQeVHy.js +0 -33
  154. package/frontend/dist/assets/GroupsPage-DHgQeVHy.js.map +0 -1
  155. package/frontend/dist/assets/MarketPage-Bvi1KESo.js +0 -2
  156. package/frontend/dist/assets/SettingsPage-DkfXDE5_.js +0 -14
  157. package/frontend/dist/assets/SettingsPage-DkfXDE5_.js.map +0 -1
  158. package/frontend/dist/assets/UsersPage-BtdFWcVD.js +0 -2
  159. package/frontend/dist/assets/UsersPage-BtdFWcVD.js.map +0 -1
  160. package/frontend/dist/assets/framework-vendor-CQFm9vKd.js +0 -61
  161. package/frontend/dist/assets/framework-vendor-CQFm9vKd.js.map +0 -1
  162. package/frontend/dist/assets/index-CkSmxNPW.js +0 -3
  163. package/frontend/dist/assets/index-CkSmxNPW.js.map +0 -1
  164. package/frontend/dist/assets/variableDetection-BUXwrSop.js +0 -18
  165. package/frontend/dist/assets/variableDetection-BUXwrSop.js.map +0 -1
@@ -1,2 +0,0 @@
1
- import{r as u,j as e,L as S}from"./framework-vendor-CQFm9vKd.js";import{h as E,i as A,j as U,l as z,a as F}from"./index-CkSmxNPW.js";import{u as N}from"./i18n-vendor-DVmjrzgW.js";import{v as k,m as P}from"./passwordValidation-DTLWgUy9.js";import{D}from"./DeleteDialog-BaCsEkJA.js";import{a as R}from"./StatusDot-00-sTQKv.js";import{R as L,P as I,l as T,X as $,U as q,K as H,w as K,T as V}from"./icons-vendor-CyAS6fj0.js";const v=()=>{const{t}=N(),[w,a]=u.useState([]),[l,j]=u.useState(!0),[b,i]=u.useState(null),[f,m]=u.useState(0),h=u.useCallback(async()=>{try{j(!0);const r=await E("/users");if(!r.success){i(r.message||t("users.fetchError"));return}r&&r.success&&Array.isArray(r.data)?a(r.data):(console.error("Invalid user data format:",r),a([])),i(null)}catch(r){console.error("Error fetching users:",r),i(r instanceof Error?r.message:"Failed to fetch users"),a([])}finally{j(!1)}},[]),o=u.useCallback(()=>{m(r=>r+1)},[]),c=async r=>{try{const s=await z("/users",r);return o(),s}catch(s){return i(s instanceof Error?s.message:"Failed to create user"),null}},g=async(r,s)=>{try{const x=await U(`/users/${r}`,s);return o(),x||null}catch(x){return i(x instanceof Error?x.message:"Failed to update user"),null}},p=async r=>{try{const s=await A(`/users/${r}`);return s!=null&&s.success?(o(),s):(i((s==null?void 0:s.message)||t("users.deleteError")),s)}catch(s){return i(s instanceof Error?s.message:"Failed to delete user"),!1}};return u.useEffect(()=>{h()},[h,f]),{users:w,loading:l,error:b,setError:i,triggerRefresh:o,createUser:c,updateUser:g,deleteUser:p}},B=({onAdd:t,onCancel:w})=>{const{t:a}=N(),{createUser:l}=v(),[j,b]=u.useState(null),[i,f]=u.useState([]),[m,h]=u.useState(!1),[o,c]=u.useState({username:"",password:"",isAdmin:!1,email:""}),g=async r=>{var x;if(r.preventDefault(),b(null),f([]),!o.username.trim()){b(a("users.usernameRequired"));return}if(!o.password.trim()){b(a("users.passwordRequired"));return}const s=k(o.password);if(!s.isValid){b(a("auth.passwordStrengthError")),f(s.errors.map(n=>a(`auth.${n}`)));return}h(!0);try{const n=await l(o);n!=null&&n.success?t():(x=n==null?void 0:n.errors)!=null&&x.length?(b(n.message||a("auth.passwordStrengthError")),f(P(n.errors).map(d=>a(d)))):b((n==null?void 0:n.message)||a("users.createError"))}catch(n){b(n instanceof Error?n.message:a("users.createError"))}finally{h(!1)}},p=r=>{const{name:s,value:x,type:n,checked:d}=r.target;c(y=>({...y,[s]:n==="checkbox"?d:x}))};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsx("div",{className:"bg-white dark:bg-gray-800 p-8 rounded-xl shadow-2xl max-w-md w-full mx-4 border border-gray-100 dark:border-gray-700",children:e.jsxs("form",{onSubmit:g,children:[e.jsx("h2",{className:"text-xl font-bold text-gray-900 mb-6",children:a("users.addNew")}),j&&e.jsxs("div",{className:"bg-red-50 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded-md",children:[e.jsx("p",{className:"text-sm font-medium",children:j}),i.length>0&&e.jsx("ul",{className:"list-disc list-inside mt-2 space-y-1 text-sm",children:i.map(r=>e.jsx("li",{children:r},r))})]}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"username",className:"block text-sm font-medium text-gray-700 mb-1",children:[a("users.username")," ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"text",id:"username",name:"username",value:o.username,onChange:p,placeholder:a("users.usernamePlaceholder"),className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200",required:!0,disabled:m})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"email",className:"block text-sm font-medium text-gray-700 mb-1",children:a("users.email")}),e.jsx("input",{type:"email",id:"email",name:"email",value:o.email||"",onChange:p,placeholder:a("users.emailPlaceholder"),className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200",disabled:m})]}),e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"password",className:"block text-sm font-medium text-gray-700 mb-1",children:[a("users.password")," ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"password",id:"password",name:"password",value:o.password,onChange:p,placeholder:a("users.passwordPlaceholder"),className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200",required:!0,disabled:m,minLength:8}),e.jsx("p",{className:"mt-1 text-xs text-gray-500",children:a("auth.passwordStrengthHint")})]}),e.jsxs("div",{className:"flex items-center pt-2",children:[e.jsx("input",{type:"checkbox",id:"isAdmin",name:"isAdmin",checked:o.isAdmin,onChange:p,className:"h-5 w-5 text-blue-600 focus:ring-blue-500 border-gray-300 rounded transition-colors duration-200",disabled:m}),e.jsx("label",{htmlFor:"isAdmin",className:"ml-3 block text-sm font-medium text-gray-700 cursor-pointer select-none",children:a("users.adminRole")})]})]}),e.jsxs("div",{className:"flex justify-end space-x-2 mt-6",children:[e.jsx("button",{type:"button",onClick:w,className:"hub-btn",disabled:m,children:a("common.cancel")}),e.jsxs("button",{type:"submit",className:"hub-btn primary",disabled:m,children:[m&&e.jsxs("svg",{className:"animate-spin -ml-1 mr-2 h-4 w-4 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[e.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),e.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),a(m?"common.creating":"users.create")]})]})]})})})},M=({user:t,onEdit:w,onCancel:a})=>{const{t:l}=N(),{updateUser:j}=v(),[b,i]=u.useState(null),[f,m]=u.useState([]),[h,o]=u.useState(!1),[c,g]=u.useState({isAdmin:t.isAdmin,email:t.email||"",newPassword:"",confirmPassword:""}),p=async s=>{var x;if(s.preventDefault(),i(null),m([]),c.newPassword&&c.newPassword!==c.confirmPassword){i(l("users.passwordMismatch"));return}if(c.newPassword){const n=k(c.newPassword);if(!n.isValid){i(l("auth.passwordStrengthError")),m(n.errors.map(d=>l(`auth.${d}`)));return}}o(!0);try{const n={isAdmin:c.isAdmin,email:c.email};c.newPassword&&(n.newPassword=c.newPassword);const d=await j(t.username,n);d!=null&&d.success?w():(x=d==null?void 0:d.errors)!=null&&x.length?(i(d.message||l("auth.passwordStrengthError")),m(P(d.errors).map(y=>l(y)))):i((d==null?void 0:d.message)||l("users.updateError"))}catch(n){i(n instanceof Error?n.message:l("users.updateError"))}finally{o(!1)}},r=s=>{const{name:x,value:n,type:d,checked:y}=s.target;g(C=>({...C,[x]:d==="checkbox"?y:n}))};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsx("div",{className:"bg-white dark:bg-gray-800 p-8 rounded-xl shadow-2xl max-w-md w-full mx-4 border border-gray-100 dark:border-gray-700",children:e.jsxs("form",{onSubmit:p,children:[e.jsxs("h2",{className:"text-xl font-bold text-gray-900 mb-6",children:[l("users.edit")," - ",e.jsx("span",{className:"text-blue-600",children:t.username})]}),b&&e.jsxs("div",{className:"bg-red-50 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded-md",children:[e.jsx("p",{className:"text-sm font-medium",children:b}),f.length>0&&e.jsx("ul",{className:"list-disc list-inside mt-2 space-y-1 text-sm",children:f.map(s=>e.jsx("li",{children:s},s))})]}),e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{className:"flex items-center pt-2",children:[e.jsx("input",{type:"checkbox",id:"isAdmin",name:"isAdmin",checked:c.isAdmin,onChange:r,className:"h-5 w-5 text-blue-600 focus:ring-blue-500 border-gray-300 rounded transition-colors duration-200",disabled:h}),e.jsx("label",{htmlFor:"isAdmin",className:"ml-3 block text-sm font-medium text-gray-700 cursor-pointer select-none",children:l("users.adminRole")})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"email",className:"block text-sm font-medium text-gray-700 mb-1",children:l("users.email")}),e.jsx("input",{type:"email",id:"email",name:"email",value:c.email,onChange:r,placeholder:l("users.emailPlaceholder"),className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200",disabled:h})]}),e.jsxs("div",{className:"border-t border-gray-100 dark:border-gray-700 pt-4 mt-2",children:[e.jsx("p",{className:"text-xs text-gray-500 uppercase font-semibold tracking-wider mb-3",children:l("users.changePassword")}),e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("label",{htmlFor:"newPassword",className:"block text-sm font-medium text-gray-700 mb-1",children:l("users.newPassword")}),e.jsx("input",{type:"password",id:"newPassword",name:"newPassword",value:c.newPassword,onChange:r,placeholder:l("users.newPasswordPlaceholder"),className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200",disabled:h,minLength:8}),e.jsx("p",{className:"mt-1 text-xs text-gray-500",children:l("auth.passwordStrengthHint")})]}),c.newPassword&&e.jsxs("div",{className:"animate-fadeIn",children:[e.jsx("label",{htmlFor:"confirmPassword",className:"block text-sm font-medium text-gray-700 mb-1",children:l("users.confirmPassword")}),e.jsx("input",{type:"password",id:"confirmPassword",name:"confirmPassword",value:c.confirmPassword,onChange:r,placeholder:l("users.confirmPasswordPlaceholder"),className:"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200",disabled:h,minLength:8})]})]})]})]}),e.jsxs("div",{className:"flex justify-end space-x-2 mt-6",children:[e.jsx("button",{type:"button",onClick:a,className:"hub-btn",disabled:h,children:l("common.cancel")}),e.jsxs("button",{type:"submit",className:"hub-btn primary",disabled:h,children:[h&&e.jsxs("svg",{className:"animate-spin -ml-1 mr-2 h-4 w-4 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[e.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),e.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),l(h?"common.updating":"users.update")]})]})]})})})},Z=()=>{const{t}=N(),{auth:w}=F(),a=w.user,{users:l,loading:j,error:b,setError:i,deleteUser:f,triggerRefresh:m}=v(),[h,o]=u.useState(null),[c,g]=u.useState(!1),[p,r]=u.useState(null);return a!=null&&a.isAdmin?e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-end justify-between gap-4 mb-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"hub-h1",children:t("pages.users.title")}),e.jsxs("p",{className:"hub-sub",children:[e.jsx("span",{className:"hub-num",children:l.length})," ",t("nav.users").toLowerCase()]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>m(),"aria-label":t("common.refresh"),children:[e.jsx(L,{size:13})," ",t("common.refresh")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>g(!0),children:[e.jsx(I,{size:13})," ",t("users.add")]})]})]}),b&&e.jsxs("div",{className:"hub-card flex items-center justify-between gap-3 mb-4",style:{padding:"10px 14px",borderColor:"oklch(0.85 0.1 25)",background:"oklch(0.97 0.03 25)",color:"oklch(0.4 0.18 25)"},children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx(T,{size:14,className:"flex-shrink-0"}),e.jsx("span",{className:"truncate text-[13px]",children:b})]}),e.jsx("button",{className:"hub-icon-btn sm",onClick:()=>i(null),children:e.jsx($,{size:13})})]}),j?e.jsx("div",{className:"hub-card p-10 text-center",style:{color:"var(--hub-ink-3)"},children:t("app.loading")}):l.length===0?e.jsx("div",{className:"hub-card p-10 text-center",style:{color:"var(--hub-ink-3)"},children:e.jsxs("div",{className:"flex flex-col items-center gap-3",children:[e.jsx("div",{className:"grid place-items-center",style:{width:40,height:40,borderRadius:10,border:"1px solid var(--hub-line)",background:"var(--hub-bg-2)"},children:e.jsx(q,{size:18})}),e.jsx("div",{className:"font-medium",style:{color:"var(--hub-ink-2)",fontSize:13},children:t("users.noUsers")}),e.jsx("button",{onClick:()=>g(!0),className:"hub-btn ghost sm",style:{color:"var(--hub-accent)"},children:t("users.addFirst")})]})}):e.jsxs("div",{className:"hub-card overflow-hidden",children:[e.jsxs("div",{className:"hub-row head hub-mono",style:{gridTemplateColumns:"1.6fr 1.2fr 100px 100px"},children:[e.jsx("div",{children:t("users.username")}),e.jsx("div",{children:t("users.email")}),e.jsx("div",{children:t("users.role")}),e.jsx("div",{className:"text-right",children:t("users.actions")})]}),l.map(s=>{const x=(a==null?void 0:a.username)===s.username;return e.jsxs("div",{className:"hub-row hover",style:{gridTemplateColumns:"1.6fr 1.2fr 100px 100px"},children:[e.jsxs("div",{className:"flex items-center gap-3 min-w-0",children:[e.jsx("div",{className:"grid place-items-center flex-shrink-0 hub-mono",style:{width:28,height:28,borderRadius:7,background:"var(--hub-bg-2)",border:"1px solid var(--hub-line)",color:"var(--hub-ink-2)",fontWeight:600,fontSize:12},children:s.username.charAt(0).toUpperCase()}),e.jsx("span",{className:"hub-mono truncate",style:{fontSize:13,color:"var(--hub-ink)"},children:s.username}),x&&e.jsx("span",{className:"hub-tag accent",style:{fontSize:10},children:t("users.currentUser")})]}),e.jsx("div",{className:"flex items-center min-w-0",children:e.jsx("span",{className:"truncate",style:{fontSize:13,color:s.email?"var(--hub-ink)":"var(--hub-ink-3)"},children:s.email||"—"})}),e.jsx("div",{children:e.jsx(R,{kind:s.isAdmin?"ok":"muted",label:s.isAdmin?t("users.admin"):t("users.user")})}),e.jsxs("div",{className:"flex justify-end gap-1",children:[e.jsx(S,{to:`/credentials?user=${encodeURIComponent(s.username)}`,className:"hub-icon-btn sm",title:t("credentials.title"),"aria-label":`${t("credentials.title")}: ${s.username}`,children:e.jsx(H,{size:13})}),e.jsx("button",{onClick:()=>o(s),className:"hub-icon-btn sm",title:t("users.edit"),children:e.jsx(K,{size:13})}),!x&&e.jsx("button",{onClick:()=>r(s.username),className:"hub-icon-btn sm",title:t("users.delete"),style:{color:"var(--hub-err)"},children:e.jsx(V,{size:13})})]})]},s.username)})]}),c&&e.jsx(B,{onAdd:()=>{g(!1),m()},onCancel:()=>g(!1)}),h&&e.jsx(M,{user:h,onEdit:()=>{o(null),m()},onCancel:()=>o(null)}),e.jsx(D,{isOpen:!!p,onClose:()=>r(null),onConfirm:async()=>{if(p){const s=await f(p);s!=null&&s.success||i((s==null?void 0:s.message)||t("users.deleteError")),r(null)}},serverName:p||"",isGroup:!1,isUser:!0})]}):e.jsx("div",{className:"hub-card p-6 text-center",style:{color:"var(--hub-err)"},children:t("users.adminRequired")})};export{Z as default};
2
- //# sourceMappingURL=UsersPage-BtdFWcVD.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"UsersPage-BtdFWcVD.js","sources":["../../src/hooks/useUserData.ts","../../src/components/AddUserForm.tsx","../../src/components/EditUserForm.tsx","../../src/pages/UsersPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { User, ApiResponse, UserFormData, UserUpdateData } from '@/types';\nimport { apiDelete, apiGet, apiPost, apiPut } from '../utils/fetchInterceptor';\n\nexport const useUserData = () => {\n const { t } = useTranslation();\n const [users, setUsers] = useState<User[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [refreshKey, setRefreshKey] = useState(0);\n\n const fetchUsers = useCallback(async () => {\n try {\n setLoading(true);\n const data: ApiResponse<User[]> = await apiGet('/users');\n if (!data.success) {\n setError(data.message || t('users.fetchError'));\n return;\n }\n\n if (data && data.success && Array.isArray(data.data)) {\n setUsers(data.data);\n } else {\n console.error('Invalid user data format:', data);\n setUsers([]);\n }\n\n setError(null);\n } catch (err) {\n console.error('Error fetching users:', err);\n setError(err instanceof Error ? err.message : 'Failed to fetch users');\n setUsers([]);\n } finally {\n setLoading(false);\n }\n }, []);\n\n // Trigger a refresh of the users data\n const triggerRefresh = useCallback(() => {\n setRefreshKey((prev) => prev + 1);\n }, []);\n\n // Create a new user\n const createUser = async (userData: UserFormData) => {\n try {\n const result: ApiResponse<User> = await apiPost('/users', userData);\n triggerRefresh();\n return result;\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create user');\n return null;\n }\n };\n\n // Update an existing user\n const updateUser = async (username: string, data: UserUpdateData) => {\n try {\n const result: ApiResponse<User> = await apiPut(`/users/${username}`, data);\n triggerRefresh();\n return result || null;\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update user');\n return null;\n }\n };\n\n // Delete a user\n const deleteUser = async (username: string) => {\n try {\n const result = await apiDelete(`/users/${username}`);\n if (!result?.success) {\n setError(result?.message || t('users.deleteError'));\n return result;\n }\n\n triggerRefresh();\n return result;\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to delete user');\n return false;\n }\n };\n\n // Fetch users when the component mounts or refreshKey changes\n useEffect(() => {\n fetchUsers();\n }, [fetchUsers, refreshKey]);\n\n return {\n users,\n loading,\n error,\n setError,\n triggerRefresh,\n createUser,\n updateUser,\n deleteUser,\n };\n};\n","import { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useUserData } from '@/hooks/useUserData';\nimport { UserFormData } from '@/types';\nimport {\n validatePasswordStrength,\n mapBackendPasswordErrors,\n} from '../utils/passwordValidation';\n\ninterface AddUserFormProps {\n onAdd: () => void;\n onCancel: () => void;\n}\n\nconst AddUserForm = ({ onAdd, onCancel }: AddUserFormProps) => {\n const { t } = useTranslation();\n const { createUser } = useUserData();\n const [error, setError] = useState<string | null>(null);\n const [passwordErrors, setPasswordErrors] = useState<string[]>([]);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const [formData, setFormData] = useState<UserFormData>({\n username: '',\n password: '',\n isAdmin: false,\n email: '',\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n setPasswordErrors([]);\n\n if (!formData.username.trim()) {\n setError(t('users.usernameRequired'));\n return;\n }\n\n if (!formData.password.trim()) {\n setError(t('users.passwordRequired'));\n return;\n }\n\n const validation = validatePasswordStrength(formData.password);\n if (!validation.isValid) {\n setError(t('auth.passwordStrengthError'));\n setPasswordErrors(validation.errors.map((key) => t(`auth.${key}`)));\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n const result = await createUser(formData);\n if (result?.success) {\n onAdd();\n } else if (result?.errors?.length) {\n setError(result.message || t('auth.passwordStrengthError'));\n setPasswordErrors(mapBackendPasswordErrors(result.errors).map((key) => t(key)));\n } else {\n setError(result?.message || t('users.createError'));\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : t('users.createError'));\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const { name, value, type, checked } = e.target;\n setFormData((prev) => ({\n ...prev,\n [name]: type === 'checkbox' ? checked : value,\n }));\n };\n\n return (\n <div className=\"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4\">\n <div className=\"bg-white dark:bg-gray-800 p-8 rounded-xl shadow-2xl max-w-md w-full mx-4 border border-gray-100 dark:border-gray-700\">\n <form onSubmit={handleSubmit}>\n <h2 className=\"text-xl font-bold text-gray-900 mb-6\">{t('users.addNew')}</h2>\n\n {error && (\n <div className=\"bg-red-50 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded-md\">\n <p className=\"text-sm font-medium\">{error}</p>\n {passwordErrors.length > 0 && (\n <ul className=\"list-disc list-inside mt-2 space-y-1 text-sm\">\n {passwordErrors.map((message) => (\n <li key={message}>{message}</li>\n ))}\n </ul>\n )}\n </div>\n )}\n\n <div className=\"space-y-5\">\n <div>\n <label htmlFor=\"username\" className=\"block text-sm font-medium text-gray-700 mb-1\">\n {t('users.username')} <span className=\"text-red-500\">*</span>\n </label>\n <input\n type=\"text\"\n id=\"username\"\n name=\"username\"\n value={formData.username}\n onChange={handleInputChange}\n placeholder={t('users.usernamePlaceholder')}\n className=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200\"\n required\n disabled={isSubmitting}\n />\n </div>\n\n <div>\n <label htmlFor=\"email\" className=\"block text-sm font-medium text-gray-700 mb-1\">\n {t('users.email')}\n </label>\n <input\n type=\"email\"\n id=\"email\"\n name=\"email\"\n value={formData.email || ''}\n onChange={handleInputChange}\n placeholder={t('users.emailPlaceholder')}\n className=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200\"\n disabled={isSubmitting}\n />\n </div>\n\n <div>\n <label htmlFor=\"password\" className=\"block text-sm font-medium text-gray-700 mb-1\">\n {t('users.password')} <span className=\"text-red-500\">*</span>\n </label>\n <input\n type=\"password\"\n id=\"password\"\n name=\"password\"\n value={formData.password}\n onChange={handleInputChange}\n placeholder={t('users.passwordPlaceholder')}\n className=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200\"\n required\n disabled={isSubmitting}\n minLength={8}\n />\n <p className=\"mt-1 text-xs text-gray-500\">\n {t('auth.passwordStrengthHint')}\n </p>\n </div>\n\n <div className=\"flex items-center pt-2\">\n <input\n type=\"checkbox\"\n id=\"isAdmin\"\n name=\"isAdmin\"\n checked={formData.isAdmin}\n onChange={handleInputChange}\n className=\"h-5 w-5 text-blue-600 focus:ring-blue-500 border-gray-300 rounded transition-colors duration-200\"\n disabled={isSubmitting}\n />\n <label\n htmlFor=\"isAdmin\"\n className=\"ml-3 block text-sm font-medium text-gray-700 cursor-pointer select-none\"\n >\n {t('users.adminRole')}\n </label>\n </div>\n </div>\n\n <div className=\"flex justify-end space-x-2 mt-6\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"hub-btn\"\n disabled={isSubmitting}\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"hub-btn primary\"\n disabled={isSubmitting}\n >\n {isSubmitting && (\n <svg\n className=\"animate-spin -ml-1 mr-2 h-4 w-4 text-white\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n ></circle>\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n )}\n {isSubmitting ? t('common.creating') : t('users.create')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\nexport default AddUserForm;\n","import { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useUserData } from '@/hooks/useUserData';\nimport { User, UserUpdateData } from '@/types';\nimport {\n validatePasswordStrength,\n mapBackendPasswordErrors,\n} from '../utils/passwordValidation';\n\ninterface EditUserFormProps {\n user: User;\n onEdit: () => void;\n onCancel: () => void;\n}\n\nconst EditUserForm = ({ user, onEdit, onCancel }: EditUserFormProps) => {\n const { t } = useTranslation();\n const { updateUser } = useUserData();\n const [error, setError] = useState<string | null>(null);\n const [passwordErrors, setPasswordErrors] = useState<string[]>([]);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const [formData, setFormData] = useState({\n isAdmin: user.isAdmin,\n email: user.email || '',\n newPassword: '',\n confirmPassword: '',\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n setPasswordErrors([]);\n\n // Validate passwords match if changing password\n if (formData.newPassword && formData.newPassword !== formData.confirmPassword) {\n setError(t('users.passwordMismatch'));\n return;\n }\n\n if (formData.newPassword) {\n const validation = validatePasswordStrength(formData.newPassword);\n if (!validation.isValid) {\n setError(t('auth.passwordStrengthError'));\n setPasswordErrors(validation.errors.map((key) => t(`auth.${key}`)));\n return;\n }\n }\n\n setIsSubmitting(true);\n\n try {\n const updateData: UserUpdateData = {\n isAdmin: formData.isAdmin,\n email: formData.email,\n };\n\n if (formData.newPassword) {\n updateData.newPassword = formData.newPassword;\n }\n\n const result = await updateUser(user.username, updateData);\n if (result?.success) {\n onEdit();\n } else if (result?.errors?.length) {\n setError(result.message || t('auth.passwordStrengthError'));\n setPasswordErrors(mapBackendPasswordErrors(result.errors).map((key) => t(key)));\n } else {\n setError(result?.message || t('users.updateError'));\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : t('users.updateError'));\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const { name, value, type, checked } = e.target;\n setFormData((prev) => ({\n ...prev,\n [name]: type === 'checkbox' ? checked : value,\n }));\n };\n\n return (\n <div className=\"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4\">\n <div className=\"bg-white dark:bg-gray-800 p-8 rounded-xl shadow-2xl max-w-md w-full mx-4 border border-gray-100 dark:border-gray-700\">\n <form onSubmit={handleSubmit}>\n <h2 className=\"text-xl font-bold text-gray-900 mb-6\">\n {t('users.edit')} - <span className=\"text-blue-600\">{user.username}</span>\n </h2>\n\n {error && (\n <div className=\"bg-red-50 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded-md\">\n <p className=\"text-sm font-medium\">{error}</p>\n {passwordErrors.length > 0 && (\n <ul className=\"list-disc list-inside mt-2 space-y-1 text-sm\">\n {passwordErrors.map((message) => (\n <li key={message}>{message}</li>\n ))}\n </ul>\n )}\n </div>\n )}\n\n <div className=\"space-y-5\">\n <div className=\"flex items-center pt-2\">\n <input\n type=\"checkbox\"\n id=\"isAdmin\"\n name=\"isAdmin\"\n checked={formData.isAdmin}\n onChange={handleInputChange}\n className=\"h-5 w-5 text-blue-600 focus:ring-blue-500 border-gray-300 rounded transition-colors duration-200\"\n disabled={isSubmitting}\n />\n <label\n htmlFor=\"isAdmin\"\n className=\"ml-3 block text-sm font-medium text-gray-700 cursor-pointer select-none\"\n >\n {t('users.adminRole')}\n </label>\n </div>\n\n <div>\n <label htmlFor=\"email\" className=\"block text-sm font-medium text-gray-700 mb-1\">\n {t('users.email')}\n </label>\n <input\n type=\"email\"\n id=\"email\"\n name=\"email\"\n value={formData.email}\n onChange={handleInputChange}\n placeholder={t('users.emailPlaceholder')}\n className=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200\"\n disabled={isSubmitting}\n />\n </div>\n\n <div className=\"border-t border-gray-100 dark:border-gray-700 pt-4 mt-2\">\n <p className=\"text-xs text-gray-500 uppercase font-semibold tracking-wider mb-3\">\n {t('users.changePassword')}\n </p>\n\n <div className=\"space-y-4\">\n <div>\n <label\n htmlFor=\"newPassword\"\n className=\"block text-sm font-medium text-gray-700 mb-1\"\n >\n {t('users.newPassword')}\n </label>\n <input\n type=\"password\"\n id=\"newPassword\"\n name=\"newPassword\"\n value={formData.newPassword}\n onChange={handleInputChange}\n placeholder={t('users.newPasswordPlaceholder')}\n className=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200\"\n disabled={isSubmitting}\n minLength={8}\n />\n <p className=\"mt-1 text-xs text-gray-500\">\n {t('auth.passwordStrengthHint')}\n </p>\n </div>\n\n {formData.newPassword && (\n <div className=\"animate-fadeIn\">\n <label\n htmlFor=\"confirmPassword\"\n className=\"block text-sm font-medium text-gray-700 mb-1\"\n >\n {t('users.confirmPassword')}\n </label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n name=\"confirmPassword\"\n value={formData.confirmPassword}\n onChange={handleInputChange}\n placeholder={t('users.confirmPasswordPlaceholder')}\n className=\"w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent form-input transition-all duration-200\"\n disabled={isSubmitting}\n minLength={8}\n />\n </div>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"flex justify-end space-x-2 mt-6\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"hub-btn\"\n disabled={isSubmitting}\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"submit\"\n className=\"hub-btn primary\"\n disabled={isSubmitting}\n >\n {isSubmitting && (\n <svg\n className=\"animate-spin -ml-1 mr-2 h-4 w-4 text-white\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n ></circle>\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n )}\n {isSubmitting ? t('common.updating') : t('users.update')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\nexport default EditUserForm;\n","import { Link } from 'react-router-dom';\nimport React, { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { User } from '@/types';\nimport { useUserData } from '@/hooks/useUserData';\nimport { useAuth } from '@/contexts/AuthContext';\nimport AddUserForm from '@/components/AddUserForm';\nimport EditUserForm from '@/components/EditUserForm';\nimport {\n KeyRound,\n Edit3,\n Trash2,\n User as UserIcon,\n Plus,\n AlertCircle,\n X,\n RefreshCw,\n} from 'lucide-react';\nimport DeleteDialog from '@/components/ui/DeleteDialog';\nimport { StatusDot } from '@/components/ui/StatusDot';\n\nconst UsersPage: React.FC = () => {\n const { t } = useTranslation();\n const { auth } = useAuth();\n const currentUser = auth.user;\n const {\n users,\n loading: usersLoading,\n error: userError,\n setError: setUserError,\n deleteUser,\n triggerRefresh,\n } = useUserData();\n\n const [editingUser, setEditingUser] = useState<User | null>(null);\n const [showAddForm, setShowAddForm] = useState(false);\n const [userToDelete, setUserToDelete] = useState<string | null>(null);\n\n if (!currentUser?.isAdmin) {\n return (\n <div className=\"hub-card p-6 text-center\" style={{ color: 'var(--hub-err)' }}>\n {t('users.adminRequired')}\n </div>\n );\n }\n\n return (\n <div>\n <div className=\"flex items-end justify-between gap-4 mb-6\">\n <div>\n <h1 className=\"hub-h1\">{t('pages.users.title')}</h1>\n <p className=\"hub-sub\">\n <span className=\"hub-num\">{users.length}</span> {t('nav.users').toLowerCase()}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <button\n className=\"hub-btn\"\n onClick={() => triggerRefresh()}\n aria-label={t('common.refresh')}\n >\n <RefreshCw size={13} /> {t('common.refresh')}\n </button>\n <button className=\"hub-btn primary\" onClick={() => setShowAddForm(true)}>\n <Plus size={13} /> {t('users.add')}\n </button>\n </div>\n </div>\n\n {userError && (\n <div\n className=\"hub-card flex items-center justify-between gap-3 mb-4\"\n style={{\n padding: '10px 14px',\n borderColor: 'oklch(0.85 0.1 25)',\n background: 'oklch(0.97 0.03 25)',\n color: 'oklch(0.4 0.18 25)',\n }}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n <AlertCircle size={14} className=\"flex-shrink-0\" />\n <span className=\"truncate text-[13px]\">{userError}</span>\n </div>\n <button className=\"hub-icon-btn sm\" onClick={() => setUserError(null)}>\n <X size={13} />\n </button>\n </div>\n )}\n\n {usersLoading ? (\n <div className=\"hub-card p-10 text-center\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('app.loading')}\n </div>\n ) : users.length === 0 ? (\n <div className=\"hub-card p-10 text-center\" style={{ color: 'var(--hub-ink-3)' }}>\n <div className=\"flex flex-col items-center gap-3\">\n <div\n className=\"grid place-items-center\"\n style={{\n width: 40,\n height: 40,\n borderRadius: 10,\n border: '1px solid var(--hub-line)',\n background: 'var(--hub-bg-2)',\n }}\n >\n <UserIcon size={18} />\n </div>\n <div className=\"font-medium\" style={{ color: 'var(--hub-ink-2)', fontSize: 13 }}>\n {t('users.noUsers')}\n </div>\n <button\n onClick={() => setShowAddForm(true)}\n className=\"hub-btn ghost sm\"\n style={{ color: 'var(--hub-accent)' }}\n >\n {t('users.addFirst')}\n </button>\n </div>\n </div>\n ) : (\n <div className=\"hub-card overflow-hidden\">\n <div\n className=\"hub-row head hub-mono\"\n style={{ gridTemplateColumns: '1.6fr 1.2fr 100px 100px' }}\n >\n <div>{t('users.username')}</div>\n <div>{t('users.email')}</div>\n <div>{t('users.role')}</div>\n <div className=\"text-right\">{t('users.actions')}</div>\n </div>\n {users.map((user) => {\n const isCurrentUser = currentUser?.username === user.username;\n return (\n <div\n key={user.username}\n className=\"hub-row hover\"\n style={{ gridTemplateColumns: '1.6fr 1.2fr 100px 100px' }}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div\n className=\"grid place-items-center flex-shrink-0 hub-mono\"\n style={{\n width: 28,\n height: 28,\n borderRadius: 7,\n background: 'var(--hub-bg-2)',\n border: '1px solid var(--hub-line)',\n color: 'var(--hub-ink-2)',\n fontWeight: 600,\n fontSize: 12,\n }}\n >\n {user.username.charAt(0).toUpperCase()}\n </div>\n <span\n className=\"hub-mono truncate\"\n style={{ fontSize: 13, color: 'var(--hub-ink)' }}\n >\n {user.username}\n </span>\n {isCurrentUser && (\n <span className=\"hub-tag accent\" style={{ fontSize: 10 }}>\n {t('users.currentUser')}\n </span>\n )}\n </div>\n <div className=\"flex items-center min-w-0\">\n <span\n className=\"truncate\"\n style={{\n fontSize: 13,\n color: user.email ? 'var(--hub-ink)' : 'var(--hub-ink-3)',\n }}\n >\n {user.email || '—'}\n </span>\n </div>\n <div>\n <StatusDot\n kind={user.isAdmin ? 'ok' : 'muted'}\n label={user.isAdmin ? t('users.admin') : t('users.user')}\n />\n </div>\n <div className=\"flex justify-end gap-1\">\n <Link\n to={`/credentials?user=${encodeURIComponent(user.username)}`}\n className=\"hub-icon-btn sm\"\n title={t('credentials.title')}\n aria-label={`${t('credentials.title')}: ${user.username}`}\n >\n <KeyRound size={13} />\n </Link>\n <button\n onClick={() => setEditingUser(user)}\n className=\"hub-icon-btn sm\"\n title={t('users.edit')}\n >\n <Edit3 size={13} />\n </button>\n {!isCurrentUser && (\n <button\n onClick={() => setUserToDelete(user.username)}\n className=\"hub-icon-btn sm\"\n title={t('users.delete')}\n style={{ color: 'var(--hub-err)' }}\n >\n <Trash2 size={13} />\n </button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {showAddForm && (\n <AddUserForm\n onAdd={() => {\n setShowAddForm(false);\n triggerRefresh();\n }}\n onCancel={() => setShowAddForm(false)}\n />\n )}\n\n {editingUser && (\n <EditUserForm\n user={editingUser}\n onEdit={() => {\n setEditingUser(null);\n triggerRefresh();\n }}\n onCancel={() => setEditingUser(null)}\n />\n )}\n\n <DeleteDialog\n isOpen={!!userToDelete}\n onClose={() => setUserToDelete(null)}\n onConfirm={async () => {\n if (userToDelete) {\n const result = await deleteUser(userToDelete);\n if (!result?.success) {\n setUserError(result?.message || t('users.deleteError'));\n }\n setUserToDelete(null);\n }\n }}\n serverName={userToDelete || ''}\n isGroup={false}\n isUser={true}\n />\n </div>\n );\n};\n\nexport default UsersPage;\n"],"names":["useUserData","useTranslation","users","setUsers","useState","loading","setLoading","error","setError","refreshKey","setRefreshKey","fetchUsers","useCallback","data","apiGet","err","triggerRefresh","prev","createUser","userData","result","apiPost","updateUser","username","apiPut","deleteUser","apiDelete","useEffect","AddUserForm","onAdd","onCancel","t","passwordErrors","setPasswordErrors","isSubmitting","setIsSubmitting","formData","setFormData","handleSubmit","e","validation","validatePasswordStrength","key","_a","mapBackendPasswordErrors","handleInputChange","name","value","type","checked","jsx","jsxs","message","EditUserForm","user","onEdit","updateData","UsersPage","auth","useAuth","currentUser","usersLoading","userError","setUserError","editingUser","setEditingUser","showAddForm","setShowAddForm","userToDelete","setUserToDelete","RefreshCw","Plus","AlertCircle","X","UserIcon","isCurrentUser","StatusDot","Link","KeyRound","Edit3","Trash2","DeleteDialog"],"mappings":"qaAKO,MAAMA,EAAc,IAAM,CAC/B,KAAM,CAAE,CAAA,EAAMC,EAAA,EACR,CAACC,EAAOC,CAAQ,EAAIC,EAAAA,SAAiB,CAAA,CAAE,EACvC,CAACC,EAASC,CAAU,EAAIF,EAAAA,SAAS,EAAI,EACrC,CAACG,EAAOC,CAAQ,EAAIJ,EAAAA,SAAwB,IAAI,EAChD,CAACK,EAAYC,CAAa,EAAIN,EAAAA,SAAS,CAAC,EAExCO,EAAaC,EAAAA,YAAY,SAAY,CACzC,GAAI,CACFN,EAAW,EAAI,EACf,MAAMO,EAA4B,MAAMC,EAAO,QAAQ,EACvD,GAAI,CAACD,EAAK,QAAS,CACjBL,EAASK,EAAK,SAAW,EAAE,kBAAkB,CAAC,EAC9C,MACF,CAEIA,GAAQA,EAAK,SAAW,MAAM,QAAQA,EAAK,IAAI,EACjDV,EAASU,EAAK,IAAI,GAElB,QAAQ,MAAM,4BAA6BA,CAAI,EAC/CV,EAAS,CAAA,CAAE,GAGbK,EAAS,IAAI,CACf,OAASO,EAAK,CACZ,QAAQ,MAAM,wBAAyBA,CAAG,EAC1CP,EAASO,aAAe,MAAQA,EAAI,QAAU,uBAAuB,EACrEZ,EAAS,CAAA,CAAE,CACb,QAAA,CACEG,EAAW,EAAK,CAClB,CACF,EAAG,CAAA,CAAE,EAGCU,EAAiBJ,EAAAA,YAAY,IAAM,CACvCF,EAAeO,GAASA,EAAO,CAAC,CAClC,EAAG,CAAA,CAAE,EAGCC,EAAa,MAAOC,GAA2B,CACnD,GAAI,CACF,MAAMC,EAA4B,MAAMC,EAAQ,SAAUF,CAAQ,EAClE,OAAAH,EAAA,EACOI,CACT,OAASL,EAAK,CACZ,OAAAP,EAASO,aAAe,MAAQA,EAAI,QAAU,uBAAuB,EAC9D,IACT,CACF,EAGMO,EAAa,MAAOC,EAAkBV,IAAyB,CACnE,GAAI,CACF,MAAMO,EAA4B,MAAMI,EAAO,UAAUD,CAAQ,GAAIV,CAAI,EACzE,OAAAG,EAAA,EACOI,GAAU,IACnB,OAASL,EAAK,CACZ,OAAAP,EAASO,aAAe,MAAQA,EAAI,QAAU,uBAAuB,EAC9D,IACT,CACF,EAGMU,EAAa,MAAOF,GAAqB,CAC7C,GAAI,CACF,MAAMH,EAAS,MAAMM,EAAU,UAAUH,CAAQ,EAAE,EACnD,OAAKH,GAAA,MAAAA,EAAQ,SAKbJ,EAAA,EACOI,IALLZ,GAASY,GAAA,YAAAA,EAAQ,UAAW,EAAE,mBAAmB,CAAC,EAC3CA,EAKX,OAASL,EAAK,CACZ,OAAAP,EAASO,aAAe,MAAQA,EAAI,QAAU,uBAAuB,EAC9D,EACT,CACF,EAGAY,OAAAA,EAAAA,UAAU,IAAM,CACdhB,EAAA,CACF,EAAG,CAACA,EAAYF,CAAU,CAAC,EAEpB,CACL,MAAAP,EACA,QAAAG,EACA,MAAAE,EACA,SAAAC,EACA,eAAAQ,EACA,WAAAE,EACA,WAAAI,EACA,WAAAG,CAAA,CAEJ,ECrFMG,EAAc,CAAC,CAAE,MAAAC,EAAO,SAAAC,KAAiC,CAC7D,KAAM,CAAE,EAAAC,CAAA,EAAM9B,EAAA,EACR,CAAE,WAAAiB,CAAA,EAAelB,EAAA,EACjB,CAACO,EAAOC,CAAQ,EAAIJ,EAAAA,SAAwB,IAAI,EAChD,CAAC4B,EAAgBC,CAAiB,EAAI7B,EAAAA,SAAmB,CAAA,CAAE,EAC3D,CAAC8B,EAAcC,CAAe,EAAI/B,EAAAA,SAAS,EAAK,EAEhD,CAACgC,EAAUC,CAAW,EAAIjC,WAAuB,CACrD,SAAU,GACV,SAAU,GACV,QAAS,GACT,MAAO,EAAA,CACR,EAEKkC,EAAe,MAAOC,GAAuB,OAKjD,GAJAA,EAAE,eAAA,EACF/B,EAAS,IAAI,EACbyB,EAAkB,CAAA,CAAE,EAEhB,CAACG,EAAS,SAAS,OAAQ,CAC7B5B,EAASuB,EAAE,wBAAwB,CAAC,EACpC,MACF,CAEA,GAAI,CAACK,EAAS,SAAS,OAAQ,CAC7B5B,EAASuB,EAAE,wBAAwB,CAAC,EACpC,MACF,CAEA,MAAMS,EAAaC,EAAyBL,EAAS,QAAQ,EAC7D,GAAI,CAACI,EAAW,QAAS,CACvBhC,EAASuB,EAAE,4BAA4B,CAAC,EACxCE,EAAkBO,EAAW,OAAO,IAAKE,GAAQX,EAAE,QAAQW,CAAG,EAAE,CAAC,CAAC,EAClE,MACF,CAEAP,EAAgB,EAAI,EAEpB,GAAI,CACF,MAAMf,EAAS,MAAMF,EAAWkB,CAAQ,EACpChB,GAAA,MAAAA,EAAQ,QACVS,EAAA,GACSc,EAAAvB,GAAA,YAAAA,EAAQ,SAAR,MAAAuB,EAAgB,QACzBnC,EAASY,EAAO,SAAWW,EAAE,4BAA4B,CAAC,EAC1DE,EAAkBW,EAAyBxB,EAAO,MAAM,EAAE,IAAKsB,GAAQX,EAAEW,CAAG,CAAC,CAAC,GAE9ElC,GAASY,GAAA,YAAAA,EAAQ,UAAWW,EAAE,mBAAmB,CAAC,CAEtD,OAAShB,EAAK,CACZP,EAASO,aAAe,MAAQA,EAAI,QAAUgB,EAAE,mBAAmB,CAAC,CACtE,QAAA,CACEI,EAAgB,EAAK,CACvB,CACF,EAEMU,EAAqBN,GAA2C,CACpE,KAAM,CAAE,KAAAO,EAAM,MAAAC,EAAO,KAAAC,EAAM,QAAAC,CAAA,EAAYV,EAAE,OACzCF,EAAapB,IAAU,CACrB,GAAGA,EACH,CAAC6B,CAAI,EAAGE,IAAS,WAAaC,EAAUF,CAAA,EACxC,CACJ,EAEA,OACEG,EAAAA,IAAC,MAAA,CAAI,UAAU,sEACb,SAAAA,EAAAA,IAAC,MAAA,CAAI,UAAU,uHACb,SAAAC,EAAAA,KAAC,OAAA,CAAK,SAAUb,EACd,SAAA,CAAAY,MAAC,KAAA,CAAG,UAAU,uCAAwC,SAAAnB,EAAE,cAAc,EAAE,EAEvExB,GACC4C,EAAAA,KAAC,MAAA,CAAI,UAAU,uEACb,SAAA,CAAAD,EAAAA,IAAC,IAAA,CAAE,UAAU,sBAAuB,SAAA3C,EAAM,EACzCyB,EAAe,OAAS,GACvBkB,EAAAA,IAAC,KAAA,CAAG,UAAU,+CACX,SAAAlB,EAAe,IAAKoB,GACnBF,EAAAA,IAAC,KAAA,CAAkB,SAAAE,CAAA,EAAVA,CAAkB,CAC5B,CAAA,CACH,CAAA,EAEJ,EAGFD,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAA,EAAAA,KAAC,QAAA,CAAM,QAAQ,WAAW,UAAU,+CACjC,SAAA,CAAApB,EAAE,gBAAgB,EAAE,IAACmB,EAAAA,IAAC,OAAA,CAAK,UAAU,eAAe,SAAA,GAAA,CAAC,CAAA,EACxD,EACAA,EAAAA,IAAC,QAAA,CACC,KAAK,OACL,GAAG,WACH,KAAK,WACL,MAAOd,EAAS,SAChB,SAAUS,EACV,YAAad,EAAE,2BAA2B,EAC1C,UAAU,yKACV,SAAQ,GACR,SAAUG,CAAA,CAAA,CACZ,EACF,SAEC,MAAA,CACC,SAAA,CAAAgB,EAAAA,IAAC,SAAM,QAAQ,QAAQ,UAAU,+CAC9B,SAAAnB,EAAE,aAAa,EAClB,EACAmB,EAAAA,IAAC,QAAA,CACC,KAAK,QACL,GAAG,QACH,KAAK,QACL,MAAOd,EAAS,OAAS,GACzB,SAAUS,EACV,YAAad,EAAE,wBAAwB,EACvC,UAAU,yKACV,SAAUG,CAAA,CAAA,CACZ,EACF,SAEC,MAAA,CACC,SAAA,CAAAiB,EAAAA,KAAC,QAAA,CAAM,QAAQ,WAAW,UAAU,+CACjC,SAAA,CAAApB,EAAE,gBAAgB,EAAE,IAACmB,EAAAA,IAAC,OAAA,CAAK,UAAU,eAAe,SAAA,GAAA,CAAC,CAAA,EACxD,EACAA,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,GAAG,WACH,KAAK,WACL,MAAOd,EAAS,SAChB,SAAUS,EACV,YAAad,EAAE,2BAA2B,EAC1C,UAAU,yKACV,SAAQ,GACR,SAAUG,EACV,UAAW,CAAA,CAAA,QAEZ,IAAA,CAAE,UAAU,6BACV,SAAAH,EAAE,2BAA2B,CAAA,CAChC,CAAA,EACF,EAEAoB,EAAAA,KAAC,MAAA,CAAI,UAAU,yBACb,SAAA,CAAAD,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,GAAG,UACH,KAAK,UACL,QAASd,EAAS,QAClB,SAAUS,EACV,UAAU,mGACV,SAAUX,CAAA,CAAA,EAEZgB,EAAAA,IAAC,QAAA,CACC,QAAQ,UACR,UAAU,0EAET,WAAE,iBAAiB,CAAA,CAAA,CACtB,CAAA,CACF,CAAA,EACF,EAEAC,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAD,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAASpB,EACT,UAAU,UACV,SAAUI,EAET,WAAE,eAAe,CAAA,CAAA,EAEpBiB,EAAAA,KAAC,SAAA,CACC,KAAK,SACL,UAAU,kBACV,SAAUjB,EAET,SAAA,CAAAA,GACCiB,EAAAA,KAAC,MAAA,CACC,UAAU,6CACV,MAAM,6BACN,KAAK,OACL,QAAQ,YAER,SAAA,CAAAD,EAAAA,IAAC,SAAA,CACC,UAAU,aACV,GAAG,KACH,GAAG,KACH,EAAE,KACF,OAAO,eACP,YAAY,GAAA,CAAA,EAEdA,EAAAA,IAAC,OAAA,CACC,UAAU,aACV,KAAK,eACL,EAAE,iHAAA,CAAA,CACH,CAAA,CAAA,EAGWnB,EAAfG,EAAiB,kBAAuB,cAAN,CAAoB,CAAA,CAAA,CACzD,CAAA,CACF,CAAA,CAAA,CACF,EACF,EACF,CAEJ,ECtMMmB,EAAe,CAAC,CAAE,KAAAC,EAAM,OAAAC,EAAQ,SAAAzB,KAAkC,CACtE,KAAM,CAAE,EAAAC,CAAA,EAAM9B,EAAA,EACR,CAAE,WAAAqB,CAAA,EAAetB,EAAA,EACjB,CAACO,EAAOC,CAAQ,EAAIJ,EAAAA,SAAwB,IAAI,EAChD,CAAC4B,EAAgBC,CAAiB,EAAI7B,EAAAA,SAAmB,CAAA,CAAE,EAC3D,CAAC8B,EAAcC,CAAe,EAAI/B,EAAAA,SAAS,EAAK,EAEhD,CAACgC,EAAUC,CAAW,EAAIjC,WAAS,CACvC,QAASkD,EAAK,QACd,MAAOA,EAAK,OAAS,GACrB,YAAa,GACb,gBAAiB,EAAA,CAClB,EAEKhB,EAAe,MAAOC,GAAuB,OAMjD,GALAA,EAAE,eAAA,EACF/B,EAAS,IAAI,EACbyB,EAAkB,CAAA,CAAE,EAGhBG,EAAS,aAAeA,EAAS,cAAgBA,EAAS,gBAAiB,CAC7E5B,EAASuB,EAAE,wBAAwB,CAAC,EACpC,MACF,CAEA,GAAIK,EAAS,YAAa,CACxB,MAAMI,EAAaC,EAAyBL,EAAS,WAAW,EAChE,GAAI,CAACI,EAAW,QAAS,CACvBhC,EAASuB,EAAE,4BAA4B,CAAC,EACxCE,EAAkBO,EAAW,OAAO,IAAKE,GAAQX,EAAE,QAAQW,CAAG,EAAE,CAAC,CAAC,EAClE,MACF,CACF,CAEAP,EAAgB,EAAI,EAEpB,GAAI,CACF,MAAMqB,EAA6B,CACjC,QAASpB,EAAS,QAClB,MAAOA,EAAS,KAAA,EAGdA,EAAS,cACXoB,EAAW,YAAcpB,EAAS,aAGpC,MAAMhB,EAAS,MAAME,EAAWgC,EAAK,SAAUE,CAAU,EACrDpC,GAAA,MAAAA,EAAQ,QACVmC,EAAA,GACSZ,EAAAvB,GAAA,YAAAA,EAAQ,SAAR,MAAAuB,EAAgB,QACzBnC,EAASY,EAAO,SAAWW,EAAE,4BAA4B,CAAC,EAC1DE,EAAkBW,EAAyBxB,EAAO,MAAM,EAAE,IAAKsB,GAAQX,EAAEW,CAAG,CAAC,CAAC,GAE9ElC,GAASY,GAAA,YAAAA,EAAQ,UAAWW,EAAE,mBAAmB,CAAC,CAEtD,OAAShB,EAAK,CACZP,EAASO,aAAe,MAAQA,EAAI,QAAUgB,EAAE,mBAAmB,CAAC,CACtE,QAAA,CACEI,EAAgB,EAAK,CACvB,CACF,EAEMU,EAAqBN,GAA2C,CACpE,KAAM,CAAE,KAAAO,EAAM,MAAAC,EAAO,KAAAC,EAAM,QAAAC,CAAA,EAAYV,EAAE,OACzCF,EAAapB,IAAU,CACrB,GAAGA,EACH,CAAC6B,CAAI,EAAGE,IAAS,WAAaC,EAAUF,CAAA,EACxC,CACJ,EAEA,OACEG,EAAAA,IAAC,MAAA,CAAI,UAAU,sEACb,SAAAA,EAAAA,IAAC,MAAA,CAAI,UAAU,uHACb,SAAAC,EAAAA,KAAC,OAAA,CAAK,SAAUb,EACd,SAAA,CAAAa,EAAAA,KAAC,KAAA,CAAG,UAAU,uCACX,SAAA,CAAApB,EAAE,YAAY,EAAE,MAAGmB,EAAAA,IAAC,OAAA,CAAK,UAAU,gBAAiB,WAAK,QAAA,CAAS,CAAA,EACrE,EAEC3C,GACC4C,EAAAA,KAAC,MAAA,CAAI,UAAU,uEACb,SAAA,CAAAD,EAAAA,IAAC,IAAA,CAAE,UAAU,sBAAuB,SAAA3C,EAAM,EACzCyB,EAAe,OAAS,GACvBkB,EAAAA,IAAC,KAAA,CAAG,UAAU,+CACX,SAAAlB,EAAe,IAAKoB,GACnBF,EAAAA,IAAC,KAAA,CAAkB,SAAAE,CAAA,EAAVA,CAAkB,CAC5B,CAAA,CACH,CAAA,EAEJ,EAGFD,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,yBACb,SAAA,CAAAD,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,GAAG,UACH,KAAK,UACL,QAASd,EAAS,QAClB,SAAUS,EACV,UAAU,mGACV,SAAUX,CAAA,CAAA,EAEZgB,EAAAA,IAAC,QAAA,CACC,QAAQ,UACR,UAAU,0EAET,WAAE,iBAAiB,CAAA,CAAA,CACtB,EACF,SAEC,MAAA,CACC,SAAA,CAAAA,EAAAA,IAAC,SAAM,QAAQ,QAAQ,UAAU,+CAC9B,SAAAnB,EAAE,aAAa,EAClB,EACAmB,EAAAA,IAAC,QAAA,CACC,KAAK,QACL,GAAG,QACH,KAAK,QACL,MAAOd,EAAS,MAChB,SAAUS,EACV,YAAad,EAAE,wBAAwB,EACvC,UAAU,yKACV,SAAUG,CAAA,CAAA,CACZ,EACF,EAEAiB,EAAAA,KAAC,MAAA,CAAI,UAAU,0DACb,SAAA,CAAAD,MAAC,IAAA,CAAE,UAAU,oEACV,SAAAnB,EAAE,sBAAsB,EAC3B,EAEAoB,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAD,EAAAA,IAAC,QAAA,CACC,QAAQ,cACR,UAAU,+CAET,WAAE,mBAAmB,CAAA,CAAA,EAExBA,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,GAAG,cACH,KAAK,cACL,MAAOd,EAAS,YAChB,SAAUS,EACV,YAAad,EAAE,8BAA8B,EAC7C,UAAU,yKACV,SAAUG,EACV,UAAW,CAAA,CAAA,QAEZ,IAAA,CAAE,UAAU,6BACV,SAAAH,EAAE,2BAA2B,CAAA,CAChC,CAAA,EACF,EAECK,EAAS,aACRe,OAAC,MAAA,CAAI,UAAU,iBACb,SAAA,CAAAD,EAAAA,IAAC,QAAA,CACC,QAAQ,kBACR,UAAU,+CAET,WAAE,uBAAuB,CAAA,CAAA,EAE5BA,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,GAAG,kBACH,KAAK,kBACL,MAAOd,EAAS,gBAChB,SAAUS,EACV,YAAad,EAAE,kCAAkC,EACjD,UAAU,yKACV,SAAUG,EACV,UAAW,CAAA,CAAA,CACb,CAAA,CACF,CAAA,CAAA,CAEJ,CAAA,CAAA,CACF,CAAA,EACF,EAEAiB,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAD,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAASpB,EACT,UAAU,UACV,SAAUI,EAET,WAAE,eAAe,CAAA,CAAA,EAEpBiB,EAAAA,KAAC,SAAA,CACC,KAAK,SACL,UAAU,kBACV,SAAUjB,EAET,SAAA,CAAAA,GACCiB,EAAAA,KAAC,MAAA,CACC,UAAU,6CACV,MAAM,6BACN,KAAK,OACL,QAAQ,YAER,SAAA,CAAAD,EAAAA,IAAC,SAAA,CACC,UAAU,aACV,GAAG,KACH,GAAG,KACH,EAAE,KACF,OAAO,eACP,YAAY,GAAA,CAAA,EAEdA,EAAAA,IAAC,OAAA,CACC,UAAU,aACV,KAAK,eACL,EAAE,iHAAA,CAAA,CACH,CAAA,CAAA,EAGWnB,EAAfG,EAAiB,kBAAuB,cAAN,CAAoB,CAAA,CAAA,CACzD,CAAA,CACF,CAAA,CAAA,CACF,EACF,EACF,CAEJ,ECzNMuB,EAAsB,IAAM,CAChC,KAAM,CAAE,CAAA,EAAMxD,EAAA,EACR,CAAE,KAAAyD,CAAA,EAASC,EAAA,EACXC,EAAcF,EAAK,KACnB,CACJ,MAAAxD,EACA,QAAS2D,EACT,MAAOC,EACP,SAAUC,EACV,WAAAtC,EACA,eAAAT,CAAA,EACEhB,EAAA,EAEE,CAACgE,EAAaC,CAAc,EAAI7D,EAAAA,SAAsB,IAAI,EAC1D,CAAC8D,EAAaC,CAAc,EAAI/D,EAAAA,SAAS,EAAK,EAC9C,CAACgE,EAAcC,CAAe,EAAIjE,EAAAA,SAAwB,IAAI,EAEpE,OAAKwD,GAAA,MAAAA,EAAa,eASf,MAAA,CACC,SAAA,CAAAT,EAAAA,KAAC,MAAA,CAAI,UAAU,4CACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAD,MAAC,KAAA,CAAG,UAAU,SAAU,SAAA,EAAE,mBAAmB,EAAE,EAC/CC,EAAAA,KAAC,IAAA,CAAE,UAAU,UACX,SAAA,CAAAD,EAAAA,IAAC,OAAA,CAAK,UAAU,UAAW,SAAAhD,EAAM,OAAO,EAAO,IAAE,EAAE,WAAW,EAAE,YAAA,CAAY,CAAA,CAC9E,CAAA,EACF,EACAiD,EAAAA,KAAC,MAAA,CAAI,UAAU,aACb,SAAA,CAAAA,EAAAA,KAAC,SAAA,CACC,UAAU,UACV,QAAS,IAAMnC,EAAA,EACf,aAAY,EAAE,gBAAgB,EAE9B,SAAA,CAAAkC,EAAAA,IAACoB,EAAA,CAAU,KAAM,EAAA,CAAI,EAAE,IAAE,EAAE,gBAAgB,CAAA,CAAA,CAAA,EAE7CnB,OAAC,UAAO,UAAU,kBAAkB,QAAS,IAAMgB,EAAe,EAAI,EACpE,SAAA,CAAAjB,EAAAA,IAACqB,EAAA,CAAK,KAAM,EAAA,CAAI,EAAE,IAAE,EAAE,WAAW,CAAA,CAAA,CACnC,CAAA,CAAA,CACF,CAAA,EACF,EAECT,GACCX,EAAAA,KAAC,MAAA,CACC,UAAU,wDACV,MAAO,CACL,QAAS,YACT,YAAa,qBACb,WAAY,sBACZ,MAAO,oBAAA,EAGT,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAD,EAAAA,IAACsB,EAAA,CAAY,KAAM,GAAI,UAAU,gBAAgB,EACjDtB,EAAAA,IAAC,OAAA,CAAK,UAAU,uBAAwB,SAAAY,CAAA,CAAU,CAAA,EACpD,EACAZ,EAAAA,IAAC,SAAA,CAAO,UAAU,kBAAkB,QAAS,IAAMa,EAAa,IAAI,EAClE,SAAAb,EAAAA,IAACuB,EAAA,CAAE,KAAM,GAAI,CAAA,CACf,CAAA,CAAA,CAAA,EAIHZ,EACCX,EAAAA,IAAC,MAAA,CAAI,UAAU,4BAA4B,MAAO,CAAE,MAAO,kBAAA,EACxD,SAAA,EAAE,aAAa,CAAA,CAClB,EACEhD,EAAM,SAAW,EACnBgD,EAAAA,IAAC,MAAA,CAAI,UAAU,4BAA4B,MAAO,CAAE,MAAO,kBAAA,EACzD,SAAAC,EAAAA,KAAC,MAAA,CAAI,UAAU,mCACb,SAAA,CAAAD,EAAAA,IAAC,MAAA,CACC,UAAU,0BACV,MAAO,CACL,MAAO,GACP,OAAQ,GACR,aAAc,GACd,OAAQ,4BACR,WAAY,iBAAA,EAGd,SAAAA,EAAAA,IAACwB,EAAA,CAAS,KAAM,EAAA,CAAI,CAAA,CAAA,EAEtBxB,EAAAA,IAAC,MAAA,CAAI,UAAU,cAAc,MAAO,CAAE,MAAO,mBAAoB,SAAU,EAAA,EACxE,SAAA,EAAE,eAAe,CAAA,CACpB,EACAA,EAAAA,IAAC,SAAA,CACC,QAAS,IAAMiB,EAAe,EAAI,EAClC,UAAU,mBACV,MAAO,CAAE,MAAO,mBAAA,EAEf,WAAE,gBAAgB,CAAA,CAAA,CACrB,CAAA,CACF,CAAA,CACF,EAEAhB,EAAAA,KAAC,MAAA,CAAI,UAAU,2BACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CACC,UAAU,wBACV,MAAO,CAAE,oBAAqB,yBAAA,EAE9B,SAAA,CAAAD,EAAAA,IAAC,MAAA,CAAK,SAAA,EAAE,gBAAgB,CAAA,CAAE,EAC1BA,EAAAA,IAAC,MAAA,CAAK,SAAA,EAAE,aAAa,CAAA,CAAE,EACvBA,EAAAA,IAAC,MAAA,CAAK,SAAA,EAAE,YAAY,CAAA,CAAE,QACrB,MAAA,CAAI,UAAU,aAAc,SAAA,EAAE,eAAe,CAAA,CAAE,CAAA,CAAA,CAAA,EAEjDhD,EAAM,IAAKoD,GAAS,CACnB,MAAMqB,GAAgBf,GAAA,YAAAA,EAAa,YAAaN,EAAK,SACrD,OACEH,EAAAA,KAAC,MAAA,CAEC,UAAU,gBACV,MAAO,CAAE,oBAAqB,yBAAA,EAE9B,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAD,EAAAA,IAAC,MAAA,CACC,UAAU,iDACV,MAAO,CACL,MAAO,GACP,OAAQ,GACR,aAAc,EACd,WAAY,kBACZ,OAAQ,4BACR,MAAO,mBACP,WAAY,IACZ,SAAU,EAAA,EAGX,SAAAI,EAAK,SAAS,OAAO,CAAC,EAAE,YAAA,CAAY,CAAA,EAEvCJ,EAAAA,IAAC,OAAA,CACC,UAAU,oBACV,MAAO,CAAE,SAAU,GAAI,MAAO,gBAAA,EAE7B,SAAAI,EAAK,QAAA,CAAA,EAEPqB,GACCzB,EAAAA,IAAC,OAAA,CAAK,UAAU,iBAAiB,MAAO,CAAE,SAAU,EAAA,EACjD,SAAA,EAAE,mBAAmB,CAAA,CACxB,CAAA,EAEJ,EACAA,EAAAA,IAAC,MAAA,CAAI,UAAU,4BACb,SAAAA,EAAAA,IAAC,OAAA,CACC,UAAU,WACV,MAAO,CACL,SAAU,GACV,MAAOI,EAAK,MAAQ,iBAAmB,kBAAA,EAGxC,WAAK,OAAS,GAAA,CAAA,EAEnB,QACC,MAAA,CACC,SAAAJ,EAAAA,IAAC0B,EAAA,CACC,KAAMtB,EAAK,QAAU,KAAO,QAC5B,MAAOA,EAAK,QAAU,EAAE,aAAa,EAAI,EAAE,YAAY,CAAA,CAAA,EAE3D,EACAH,EAAAA,KAAC,MAAA,CAAI,UAAU,yBACb,SAAA,CAAAD,EAAAA,IAAC2B,EAAA,CACC,GAAI,qBAAqB,mBAAmBvB,EAAK,QAAQ,CAAC,GAC1D,UAAU,kBACV,MAAO,EAAE,mBAAmB,EAC5B,aAAY,GAAG,EAAE,mBAAmB,CAAC,KAAKA,EAAK,QAAQ,GAEvD,SAAAJ,EAAAA,IAAC4B,EAAA,CAAS,KAAM,EAAA,CAAI,CAAA,CAAA,EAEtB5B,EAAAA,IAAC,SAAA,CACC,QAAS,IAAMe,EAAeX,CAAI,EAClC,UAAU,kBACV,MAAO,EAAE,YAAY,EAErB,SAAAJ,EAAAA,IAAC6B,EAAA,CAAM,KAAM,EAAA,CAAI,CAAA,CAAA,EAElB,CAACJ,GACAzB,EAAAA,IAAC,SAAA,CACC,QAAS,IAAMmB,EAAgBf,EAAK,QAAQ,EAC5C,UAAU,kBACV,MAAO,EAAE,cAAc,EACvB,MAAO,CAAE,MAAO,gBAAA,EAEhB,SAAAJ,EAAAA,IAAC8B,EAAA,CAAO,KAAM,EAAA,CAAI,CAAA,CAAA,CACpB,CAAA,CAEJ,CAAA,CAAA,EA3EK1B,EAAK,QAAA,CA8EhB,CAAC,CAAA,EACH,EAGDY,GACChB,EAAAA,IAACtB,EAAA,CACC,MAAO,IAAM,CACXuC,EAAe,EAAK,EACpBnD,EAAA,CACF,EACA,SAAU,IAAMmD,EAAe,EAAK,CAAA,CAAA,EAIvCH,GACCd,EAAAA,IAACG,EAAA,CACC,KAAMW,EACN,OAAQ,IAAM,CACZC,EAAe,IAAI,EACnBjD,EAAA,CACF,EACA,SAAU,IAAMiD,EAAe,IAAI,CAAA,CAAA,EAIvCf,EAAAA,IAAC+B,EAAA,CACC,OAAQ,CAAC,CAACb,EACV,QAAS,IAAMC,EAAgB,IAAI,EACnC,UAAW,SAAY,CACrB,GAAID,EAAc,CAChB,MAAMhD,EAAS,MAAMK,EAAW2C,CAAY,EACvChD,GAAA,MAAAA,EAAQ,SACX2C,GAAa3C,GAAA,YAAAA,EAAQ,UAAW,EAAE,mBAAmB,CAAC,EAExDiD,EAAgB,IAAI,CACtB,CACF,EACA,WAAYD,GAAgB,GAC5B,QAAS,GACT,OAAQ,EAAA,CAAA,CACV,EACF,EAtNElB,EAAAA,IAAC,MAAA,CAAI,UAAU,2BAA2B,MAAO,CAAE,MAAO,gBAAA,EACvD,SAAA,EAAE,qBAAqB,CAAA,CAC1B,CAsNN"}