@samanhappy/mcphub 1.1.14 → 1.2.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/README.fr.md +2 -1
- package/README.md +2 -1
- package/README.zh.md +2 -1
- package/dist/betterAuth.js +3 -3
- package/dist/betterAuth.js.map +1 -1
- package/dist/cli/commands/keys.js +22 -3
- package/dist/cli/commands/keys.js.map +1 -1
- package/dist/clients/mcpSdkClient.js +2667 -1448
- package/dist/clients/mcpSdkClient.js.map +4 -4
- package/dist/clients/openapi.js +3 -2
- package/dist/clients/openapi.js.map +1 -1
- package/dist/controllers/bearerKeyController.js +49 -3
- package/dist/controllers/bearerKeyController.js.map +1 -1
- package/dist/controllers/credentialBindingController.js +1 -1
- package/dist/controllers/credentialBindingController.js.map +1 -1
- package/dist/controllers/groupController.js +12 -5
- package/dist/controllers/groupController.js.map +1 -1
- package/dist/controllers/healthController.js +23 -1
- package/dist/controllers/healthController.js.map +1 -1
- package/dist/controllers/metricsController.js +9 -1
- package/dist/controllers/metricsController.js.map +1 -1
- package/dist/controllers/oauthDynamicRegistrationController.js +5 -5
- package/dist/controllers/oauthDynamicRegistrationController.js.map +1 -1
- package/dist/controllers/oauthRelayController.js +65 -0
- package/dist/controllers/oauthRelayController.js.map +1 -0
- package/dist/controllers/personalOAuthController.js +10 -1
- package/dist/controllers/personalOAuthController.js.map +1 -1
- package/dist/controllers/serverController.js +23 -14
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/controllers/userController.js +11 -1
- package/dist/controllers/userController.js.map +1 -1
- package/dist/dao/BearerKeyDao.js +10 -5
- package/dist/dao/BearerKeyDao.js.map +1 -1
- package/dist/dao/BearerKeyDaoDbImpl.js +16 -5
- package/dist/dao/BearerKeyDaoDbImpl.js.map +1 -1
- package/dist/dao/GroupDaoDbImpl.js +11 -0
- package/dist/dao/GroupDaoDbImpl.js.map +1 -1
- package/dist/dao/ServerDao.js +5 -1
- package/dist/dao/ServerDao.js.map +1 -1
- package/dist/dao/ServerDaoDbImpl.js +3 -0
- package/dist/dao/ServerDaoDbImpl.js.map +1 -1
- package/dist/dao/UserDaoDbImpl.js +4 -0
- package/dist/dao/UserDaoDbImpl.js.map +1 -1
- package/dist/db/connection.js +6 -2
- package/dist/db/connection.js.map +1 -1
- package/dist/db/entities/BearerKey.js +8 -0
- package/dist/db/entities/BearerKey.js.map +1 -1
- package/dist/db/entities/Group.js +4 -0
- package/dist/db/entities/Group.js.map +1 -1
- package/dist/db/entities/OAuthState.js +43 -0
- package/dist/db/entities/OAuthState.js.map +1 -0
- package/dist/db/entities/Server.js +4 -0
- package/dist/db/entities/Server.js.map +1 -1
- package/dist/db/entities/User.js +4 -0
- package/dist/db/entities/User.js.map +1 -1
- package/dist/db/entities/index.js +3 -1
- package/dist/db/entities/index.js.map +1 -1
- package/dist/db/replicaSchema.js +40 -0
- package/dist/db/replicaSchema.js.map +1 -0
- package/dist/db/repositories/ServerRepository.js +1 -0
- package/dist/db/repositories/ServerRepository.js.map +1 -1
- package/dist/db/setup.js +25 -0
- package/dist/db/setup.js.map +1 -0
- package/dist/db/transactionContext.js +21 -0
- package/dist/db/transactionContext.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/middlewares/auth.js +7 -1
- package/dist/middlewares/auth.js.map +1 -1
- package/dist/middlewares/replicaConfig.js +134 -0
- package/dist/middlewares/replicaConfig.js.map +1 -0
- package/dist/middlewares/userContext.js +1 -1
- package/dist/middlewares/userContext.js.map +1 -1
- package/dist/models/OAuth.js +57 -7
- package/dist/models/OAuth.js.map +1 -1
- package/dist/routes/index.js +16 -1
- package/dist/routes/index.js.map +1 -1
- package/dist/server.js +13 -4
- package/dist/server.js.map +1 -1
- package/dist/services/authorizationService.js.map +1 -1
- package/dist/services/capabilitySnapshotService.js +2 -1
- package/dist/services/capabilitySnapshotService.js.map +1 -1
- package/dist/services/credentialBindingService.js +13 -1
- package/dist/services/credentialBindingService.js.map +1 -1
- package/dist/services/dataService.js +4 -2
- package/dist/services/dataService.js.map +1 -1
- package/dist/services/groupService.js +15 -7
- package/dist/services/groupService.js.map +1 -1
- package/dist/services/mcpEventBus.js +6 -0
- package/dist/services/mcpEventBus.js.map +1 -0
- package/dist/services/mcpOAuthProvider.js +91 -5
- package/dist/services/mcpOAuthProvider.js.map +1 -1
- package/dist/services/mcpService.js +136 -9
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/metricsService.js +4 -0
- package/dist/services/metricsService.js.map +1 -1
- package/dist/services/oauthClientRegistration.js +21 -1
- package/dist/services/oauthClientRegistration.js.map +1 -1
- package/dist/services/oauthServerService.js +5 -6
- package/dist/services/oauthServerService.js.map +1 -1
- package/dist/services/oauthSettingsStore.js +21 -0
- package/dist/services/oauthSettingsStore.js.map +1 -1
- package/dist/services/personalOAuthService.js +42 -2
- package/dist/services/personalOAuthService.js.map +1 -1
- package/dist/services/replicaLock.js +30 -0
- package/dist/services/replicaLock.js.map +1 -0
- package/dist/services/replicaService.js +111 -0
- package/dist/services/replicaService.js.map +1 -0
- package/dist/services/requestContextService.js +15 -0
- package/dist/services/requestContextService.js.map +1 -1
- package/dist/services/serverConfigEnvironment.js +2 -1
- package/dist/services/serverConfigEnvironment.js.map +1 -1
- package/dist/services/sseService.js +61 -8
- package/dist/services/sseService.js.map +1 -1
- package/dist/services/templateService.js +11 -5
- package/dist/services/templateService.js.map +1 -1
- package/dist/services/userService.js +5 -1
- package/dist/services/userService.js.map +1 -1
- package/dist/utils/groupAccess.js +6 -4
- package/dist/utils/groupAccess.js.map +1 -1
- package/dist/utils/groupKeyScope.js +11 -5
- package/dist/utils/groupKeyScope.js.map +1 -1
- package/dist/utils/logger.js +7 -0
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/migration.js +8 -0
- package/dist/utils/migration.js.map +1 -1
- package/dist/utils/oauthBearer.js +1 -0
- package/dist/utils/oauthBearer.js.map +1 -1
- package/dist/utils/oauthServerMetadata.js +21 -0
- package/dist/utils/oauthServerMetadata.js.map +1 -1
- package/dist/utils/replicaLogContext.js +3 -0
- package/dist/utils/replicaLogContext.js.map +1 -0
- package/dist/utils/replicaMode.js +17 -0
- package/dist/utils/replicaMode.js.map +1 -0
- package/dist/utils/serverConfigPersistence.js +20 -1
- package/dist/utils/serverConfigPersistence.js.map +1 -1
- package/dist/utils/serverConfigValidation.js +1 -0
- package/dist/utils/serverConfigValidation.js.map +1 -1
- package/dist/utils/settingsSync.js +4 -0
- package/dist/utils/settingsSync.js.map +1 -1
- package/frontend/dist/assets/{ActivityPage-C8Zj4FeE.js → ActivityPage-CSXEpl6y.js} +2 -2
- package/frontend/dist/assets/{ActivityPage-C8Zj4FeE.js.map → ActivityPage-CSXEpl6y.js.map} +1 -1
- package/frontend/dist/assets/{ConfirmDialog-BYdfb115.js → ConfirmDialog-BJpzQmMw.js} +2 -2
- package/frontend/dist/assets/{ConfirmDialog-BYdfb115.js.map → ConfirmDialog-BJpzQmMw.js.map} +1 -1
- package/frontend/dist/assets/CredentialsPage-CPxOHQUZ.js +2 -0
- package/frontend/dist/assets/CredentialsPage-CPxOHQUZ.js.map +1 -0
- package/frontend/dist/assets/{Dashboard-CCiZcGi4.js → Dashboard-Da-YT_l_.js} +2 -2
- package/frontend/dist/assets/{Dashboard-CCiZcGi4.js.map → Dashboard-Da-YT_l_.js.map} +1 -1
- package/frontend/dist/assets/{DeleteDialog-BaCsEkJA.js → DeleteDialog-BoMYLx3J.js} +2 -2
- package/frontend/dist/assets/{DeleteDialog-BaCsEkJA.js.map → DeleteDialog-BoMYLx3J.js.map} +1 -1
- package/frontend/dist/assets/{EndpointCopy-CmKx4RVL.js → EndpointCopy-CZOjiFAH.js} +2 -2
- package/frontend/dist/assets/{EndpointCopy-CmKx4RVL.js.map → EndpointCopy-CZOjiFAH.js.map} +1 -1
- package/frontend/dist/assets/GroupsPage-CSA5G9_H.js +33 -0
- package/frontend/dist/assets/GroupsPage-CSA5G9_H.js.map +1 -0
- package/frontend/dist/assets/{LoginPage-Hn1uu0vB.js → LoginPage-7RswUPtG.js} +2 -2
- package/frontend/dist/assets/{LoginPage-Hn1uu0vB.js.map → LoginPage-7RswUPtG.js.map} +1 -1
- package/frontend/dist/assets/{LogsPage-JMCtTjCv.js → LogsPage-Dbef_68F.js} +2 -2
- package/frontend/dist/assets/{LogsPage-JMCtTjCv.js.map → LogsPage-Dbef_68F.js.map} +1 -1
- package/frontend/dist/assets/MarketPage-rSBdHZxo.js +2 -0
- package/frontend/dist/assets/{MarketPage-Bvi1KESo.js.map → MarketPage-rSBdHZxo.js.map} +1 -1
- package/frontend/dist/assets/{OAuthConsentPage-DofQEZAU.js → OAuthConsentPage-BH9O7bk3.js} +2 -2
- package/frontend/dist/assets/{OAuthConsentPage-DofQEZAU.js.map → OAuthConsentPage-BH9O7bk3.js.map} +1 -1
- package/frontend/dist/assets/{Pagination-D99WHhBx.js → Pagination-S-7lzy59.js} +2 -2
- package/frontend/dist/assets/{Pagination-D99WHhBx.js.map → Pagination-S-7lzy59.js.map} +1 -1
- package/frontend/dist/assets/{PromptsPage-B-8vczAR.js → PromptsPage-B558MhVU.js} +2 -2
- package/frontend/dist/assets/{PromptsPage-B-8vczAR.js.map → PromptsPage-B558MhVU.js.map} +1 -1
- package/frontend/dist/assets/{ResourcesPage-DLAIatoG.js → ResourcesPage-DQpy8B9L.js} +2 -2
- package/frontend/dist/assets/{ResourcesPage-DLAIatoG.js.map → ResourcesPage-DQpy8B9L.js.map} +1 -1
- package/frontend/dist/assets/{ServersPage-wVLsTL_f.js → ServersPage-DBcC8pG2.js} +5 -5
- package/frontend/dist/assets/ServersPage-DBcC8pG2.js.map +1 -0
- package/frontend/dist/assets/SettingsPage-BcpeHFR3.js +14 -0
- package/frontend/dist/assets/SettingsPage-BcpeHFR3.js.map +1 -0
- package/frontend/dist/assets/{StatusDot-00-sTQKv.js → StatusDot-CLxRrJ5l.js} +2 -2
- package/frontend/dist/assets/{StatusDot-00-sTQKv.js.map → StatusDot-CLxRrJ5l.js.map} +1 -1
- package/frontend/dist/assets/{ToggleGroup-CtWBng3r.js → ToggleGroup-DuWJZU41.js} +2 -2
- package/frontend/dist/assets/{ToggleGroup-CtWBng3r.js.map → ToggleGroup-DuWJZU41.js.map} +1 -1
- package/frontend/dist/assets/UserGroupSelection-CN0weGXJ.js +2 -0
- package/frontend/dist/assets/UserGroupSelection-CN0weGXJ.js.map +1 -0
- package/frontend/dist/assets/UsersPage-B158Bofs.js +2 -0
- package/frontend/dist/assets/UsersPage-B158Bofs.js.map +1 -0
- package/frontend/dist/assets/{clipboard-2RRFM-q8.js → clipboard-BLr_gbf8.js} +2 -2
- package/frontend/dist/assets/{clipboard-2RRFM-q8.js.map → clipboard-BLr_gbf8.js.map} +1 -1
- package/frontend/dist/assets/{costRevision-Ntl7VtWo.js → costRevision-Dzec2k_b.js} +2 -2
- package/frontend/dist/assets/{costRevision-Ntl7VtWo.js.map → costRevision-Dzec2k_b.js.map} +1 -1
- package/frontend/dist/assets/framework-vendor-D0S_ys6r.js +61 -0
- package/frontend/dist/assets/framework-vendor-D0S_ys6r.js.map +1 -0
- package/frontend/dist/assets/{i18n-vendor-DVmjrzgW.js → i18n-vendor-COTriSc6.js} +2 -2
- package/frontend/dist/assets/{i18n-vendor-DVmjrzgW.js.map → i18n-vendor-COTriSc6.js.map} +1 -1
- package/frontend/dist/assets/{icons-vendor-CyAS6fj0.js → icons-vendor-DZjjGrOu.js} +2 -2
- package/frontend/dist/assets/{icons-vendor-CyAS6fj0.js.map → icons-vendor-DZjjGrOu.js.map} +1 -1
- package/frontend/dist/assets/index-CGRjM_NC.js +3 -0
- package/frontend/dist/assets/{index-CkSmxNPW.js.map → index-CGRjM_NC.js.map} +1 -1
- package/frontend/dist/assets/{resourceService-BB4UM6IQ.js → resourceService-BNH7uNar.js} +2 -2
- package/frontend/dist/assets/{resourceService-BB4UM6IQ.js.map → resourceService-BNH7uNar.js.map} +1 -1
- package/frontend/dist/assets/{useCostData-C3XnQyUj.js → useCostData-B2d2eLCD.js} +2 -2
- package/frontend/dist/assets/{useCostData-C3XnQyUj.js.map → useCostData-B2d2eLCD.js.map} +1 -1
- package/frontend/dist/assets/variableDetection-DRdE941j.js +80 -0
- package/frontend/dist/assets/variableDetection-DRdE941j.js.map +1 -0
- package/frontend/dist/index.html +4 -4
- package/package.json +4 -2
- package/frontend/dist/assets/CredentialsPage-BnzqPYjg.js +0 -2
- package/frontend/dist/assets/CredentialsPage-BnzqPYjg.js.map +0 -1
- package/frontend/dist/assets/GroupsPage-DHgQeVHy.js +0 -33
- package/frontend/dist/assets/GroupsPage-DHgQeVHy.js.map +0 -1
- package/frontend/dist/assets/MarketPage-Bvi1KESo.js +0 -2
- package/frontend/dist/assets/ServersPage-wVLsTL_f.js.map +0 -1
- package/frontend/dist/assets/SettingsPage-DkfXDE5_.js +0 -14
- package/frontend/dist/assets/SettingsPage-DkfXDE5_.js.map +0 -1
- package/frontend/dist/assets/UsersPage-BtdFWcVD.js +0 -2
- package/frontend/dist/assets/UsersPage-BtdFWcVD.js.map +0 -1
- package/frontend/dist/assets/framework-vendor-CQFm9vKd.js +0 -61
- package/frontend/dist/assets/framework-vendor-CQFm9vKd.js.map +0 -1
- package/frontend/dist/assets/index-CkSmxNPW.js +0 -3
- package/frontend/dist/assets/variableDetection-BUXwrSop.js +0 -18
- 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"}
|