@samanhappy/mcphub 1.0.0 → 1.0.2

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 (133) hide show
  1. package/README.fr.md +6 -4
  2. package/README.md +24 -4
  3. package/README.zh.md +25 -4
  4. package/bin/cli.js +64 -50
  5. package/dist/betterAuth.js +21 -0
  6. package/dist/betterAuth.js.map +1 -1
  7. package/dist/cli/call-arguments.js +81 -0
  8. package/dist/cli/call-arguments.js.map +1 -0
  9. package/dist/cli/commands/call.js +75 -0
  10. package/dist/cli/commands/call.js.map +1 -0
  11. package/dist/cli/commands/config.js +132 -0
  12. package/dist/cli/commands/config.js.map +1 -0
  13. package/dist/cli/commands/discover.js +127 -0
  14. package/dist/cli/commands/discover.js.map +1 -0
  15. package/dist/cli/commands/export.js +20 -0
  16. package/dist/cli/commands/export.js.map +1 -0
  17. package/dist/cli/commands/groups.js +107 -0
  18. package/dist/cli/commands/groups.js.map +1 -0
  19. package/dist/cli/commands/install.js +173 -0
  20. package/dist/cli/commands/install.js.map +1 -0
  21. package/dist/cli/commands/keys.js +91 -0
  22. package/dist/cli/commands/keys.js.map +1 -0
  23. package/dist/cli/commands/login.js +70 -0
  24. package/dist/cli/commands/login.js.map +1 -0
  25. package/dist/cli/commands/servers.js +142 -0
  26. package/dist/cli/commands/servers.js.map +1 -0
  27. package/dist/cli/commands/tools.js +162 -0
  28. package/dist/cli/commands/tools.js.map +1 -0
  29. package/dist/cli/context.js +44 -0
  30. package/dist/cli/context.js.map +1 -0
  31. package/dist/cli/errors.js +19 -0
  32. package/dist/cli/errors.js.map +1 -0
  33. package/dist/cli/help.js +157 -0
  34. package/dist/cli/help.js.map +1 -0
  35. package/dist/cli/http.js +93 -0
  36. package/dist/cli/http.js.map +1 -0
  37. package/dist/cli/main.js +81 -0
  38. package/dist/cli/main.js.map +1 -0
  39. package/dist/cli/output.js +47 -0
  40. package/dist/cli/output.js.map +1 -0
  41. package/dist/cli/parse-args.js +103 -0
  42. package/dist/cli/parse-args.js.map +1 -0
  43. package/dist/cli/profile.js +109 -0
  44. package/dist/cli/profile.js.map +1 -0
  45. package/dist/cli/prompts.js +56 -0
  46. package/dist/cli/prompts.js.map +1 -0
  47. package/dist/controllers/oauthServerController.js +37 -1
  48. package/dist/controllers/oauthServerController.js.map +1 -1
  49. package/dist/controllers/serverController.js +3 -0
  50. package/dist/controllers/serverController.js.map +1 -1
  51. package/dist/dao/ActivityDao.js +2 -0
  52. package/dist/dao/ActivityDao.js.map +1 -1
  53. package/dist/dao/ServerDaoDbImpl.js +3 -0
  54. package/dist/dao/ServerDaoDbImpl.js.map +1 -1
  55. package/dist/db/connection.js +48 -4
  56. package/dist/db/connection.js.map +1 -1
  57. package/dist/db/entities/Activity.js +4 -0
  58. package/dist/db/entities/Activity.js.map +1 -1
  59. package/dist/db/entities/Server.js +4 -0
  60. package/dist/db/entities/Server.js.map +1 -1
  61. package/dist/db/entities/VectorEmbedding.js +2 -5
  62. package/dist/db/entities/VectorEmbedding.js.map +1 -1
  63. package/dist/db/repositories/VectorEmbeddingRepository.js +100 -34
  64. package/dist/db/repositories/VectorEmbeddingRepository.js.map +1 -1
  65. package/dist/services/activityLoggingService.js +1 -0
  66. package/dist/services/activityLoggingService.js.map +1 -1
  67. package/dist/services/betterAuthConfig.js +18 -1
  68. package/dist/services/betterAuthConfig.js.map +1 -1
  69. package/dist/services/dataService.js +10 -1
  70. package/dist/services/dataService.js.map +1 -1
  71. package/dist/services/mcpService.js +46 -12
  72. package/dist/services/mcpService.js.map +1 -1
  73. package/dist/services/vectorSearchService.js +16 -3
  74. package/dist/services/vectorSearchService.js.map +1 -1
  75. package/dist/utils/migration.js +1 -0
  76. package/dist/utils/migration.js.map +1 -1
  77. package/dist/utils/rateLimit.js +3 -3
  78. package/dist/utils/serverConfigPersistence.js +5 -0
  79. package/dist/utils/serverConfigPersistence.js.map +1 -1
  80. package/frontend/dist/assets/ActivityPage-B1B9ySGe.js +2 -0
  81. package/frontend/dist/assets/ActivityPage-B1B9ySGe.js.map +1 -0
  82. package/frontend/dist/assets/Dashboard-CYzpYZ1d.js +2 -0
  83. package/frontend/dist/assets/Dashboard-CYzpYZ1d.js.map +1 -0
  84. package/frontend/dist/assets/{EndpointCopy-C59moJ3Y.js → EndpointCopy-DVmlWW-s.js} +2 -2
  85. package/frontend/dist/assets/{EndpointCopy-C59moJ3Y.js.map → EndpointCopy-DVmlWW-s.js.map} +1 -1
  86. package/frontend/dist/assets/{GroupsPage-Bhg51kbu.js → GroupsPage-CsyoUc8S.js} +2 -2
  87. package/frontend/dist/assets/{GroupsPage-Bhg51kbu.js.map → GroupsPage-CsyoUc8S.js.map} +1 -1
  88. package/frontend/dist/assets/LoginPage-C3t8hbb2.js +2 -0
  89. package/frontend/dist/assets/LoginPage-C3t8hbb2.js.map +1 -0
  90. package/frontend/dist/assets/{LogsPage-HBGNLtyN.js → LogsPage-DOZeagVs.js} +2 -2
  91. package/frontend/dist/assets/{LogsPage-HBGNLtyN.js.map → LogsPage-DOZeagVs.js.map} +1 -1
  92. package/frontend/dist/assets/{MarketPage-C5ATZ4WS.js → MarketPage-kMEHUGfJ.js} +2 -2
  93. package/frontend/dist/assets/{MarketPage-C5ATZ4WS.js.map → MarketPage-kMEHUGfJ.js.map} +1 -1
  94. package/frontend/dist/assets/{PromptsPage-DUge8OO4.js → PromptsPage-D7JYoTav.js} +2 -2
  95. package/frontend/dist/assets/{PromptsPage-DUge8OO4.js.map → PromptsPage-D7JYoTav.js.map} +1 -1
  96. package/frontend/dist/assets/{ResourcesPage-5J3JYGVC.js → ResourcesPage-BlwePI9a.js} +2 -2
  97. package/frontend/dist/assets/{ResourcesPage-5J3JYGVC.js.map → ResourcesPage-BlwePI9a.js.map} +1 -1
  98. package/frontend/dist/assets/ServersPage-DrkPpCgK.js +37 -0
  99. package/frontend/dist/assets/ServersPage-DrkPpCgK.js.map +1 -0
  100. package/frontend/dist/assets/{SettingsPage-BeLZKC1d.js → SettingsPage-BWigWLml.js} +2 -2
  101. package/frontend/dist/assets/{SettingsPage-BeLZKC1d.js.map → SettingsPage-BWigWLml.js.map} +1 -1
  102. package/frontend/dist/assets/{StatusDot-DR803YdX.js → StatusDot-BDBIaafQ.js} +2 -2
  103. package/frontend/dist/assets/{StatusDot-DR803YdX.js.map → StatusDot-BDBIaafQ.js.map} +1 -1
  104. package/frontend/dist/assets/{ToggleGroup-DE8t8Ni4.js → ToggleGroup-OOcsllhw.js} +2 -2
  105. package/frontend/dist/assets/{ToggleGroup-DE8t8Ni4.js.map → ToggleGroup-OOcsllhw.js.map} +1 -1
  106. package/frontend/dist/assets/{UsersPage-CMscqAmn.js → UsersPage-DL8E7KtW.js} +2 -2
  107. package/frontend/dist/assets/{UsersPage-CMscqAmn.js.map → UsersPage-DL8E7KtW.js.map} +1 -1
  108. package/frontend/dist/assets/index-C7wNc_3N.js +3 -0
  109. package/frontend/dist/assets/index-C7wNc_3N.js.map +1 -0
  110. package/frontend/dist/assets/index-D0OIBhmN.css +1 -0
  111. package/frontend/dist/assets/{resourceService-BFMkDDIh.js → resourceService-B-U4FKGB.js} +2 -2
  112. package/frontend/dist/assets/{resourceService-BFMkDDIh.js.map → resourceService-B-U4FKGB.js.map} +1 -1
  113. package/frontend/dist/assets/{useServerData-BnLmpLAC.js → useServerData-DYoDryJj.js} +2 -2
  114. package/frontend/dist/assets/{useServerData-BnLmpLAC.js.map → useServerData-DYoDryJj.js.map} +1 -1
  115. package/frontend/dist/assets/useSettingsData-6utb1Z46.js +2 -0
  116. package/frontend/dist/assets/{useSettingsData-Cqi9d7Ug.js.map → useSettingsData-6utb1Z46.js.map} +1 -1
  117. package/frontend/dist/assets/{variableDetection-BigiltQM.js → variableDetection-DsYuiOB_.js} +3 -3
  118. package/frontend/dist/assets/variableDetection-DsYuiOB_.js.map +1 -0
  119. package/frontend/dist/index.html +2 -2
  120. package/package.json +2 -1
  121. package/frontend/dist/assets/ActivityPage-VwilVMvp.js +0 -2
  122. package/frontend/dist/assets/ActivityPage-VwilVMvp.js.map +0 -1
  123. package/frontend/dist/assets/Dashboard-DuBJTbbA.js +0 -2
  124. package/frontend/dist/assets/Dashboard-DuBJTbbA.js.map +0 -1
  125. package/frontend/dist/assets/LoginPage-C8RkMoJN.js +0 -2
  126. package/frontend/dist/assets/LoginPage-C8RkMoJN.js.map +0 -1
  127. package/frontend/dist/assets/ServersPage-DtnlfwJF.js +0 -37
  128. package/frontend/dist/assets/ServersPage-DtnlfwJF.js.map +0 -1
  129. package/frontend/dist/assets/index-B9cW2F0H.js +0 -3
  130. package/frontend/dist/assets/index-B9cW2F0H.js.map +0 -1
  131. package/frontend/dist/assets/index-Crcbkt8x.css +0 -1
  132. package/frontend/dist/assets/useSettingsData-Cqi9d7Ug.js +0 -2
  133. package/frontend/dist/assets/variableDetection-BigiltQM.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{j as e}from"./framework-vendor-BUhDPOUZ.js";import{m as d}from"./index-B9cW2F0H.js";import{u}from"./i18n-vendor-Kbr87Ofu.js";const l={connected:"ok",connecting:"warn",oauth_required:"warn",disconnected:"err"},h={connected:"status.online",connecting:"status.connecting",oauth_required:"status.oauthRequired",disconnected:"status.offline"},a=({kind:n,label:r,className:o,onClick:t,title:s})=>e.jsxs("span",{className:d("hub-status",n,t?"cursor-pointer hover:opacity-80":"",o),onClick:t,title:s,children:[e.jsx("span",{className:"hub-dot"}),r!=null&&e.jsx("span",{children:r})]}),j=({status:n,enabled:r,onAuthClick:o,className:t})=>{const{t:s}=u();if(r===!1)return e.jsx(a,{kind:"muted",label:s("server.disable")||"Disabled",className:t});const c=l[n]??"muted",i=n==="oauth_required";return e.jsx(a,{kind:c,label:e.jsxs(e.Fragment,{children:[i&&e.jsx("span",{"aria-hidden":!0,children:"🔐"}),e.jsx("span",{children:s(h[n]||n)})]}),onClick:i&&o?o:void 0,title:i?s("status.clickToAuthorize"):void 0,className:t})};export{j as S,a};
2
- //# sourceMappingURL=StatusDot-DR803YdX.js.map
1
+ import{j as e}from"./framework-vendor-BUhDPOUZ.js";import{m as d}from"./index-C7wNc_3N.js";import{u}from"./i18n-vendor-Kbr87Ofu.js";const l={connected:"ok",connecting:"warn",oauth_required:"warn",disconnected:"err"},h={connected:"status.online",connecting:"status.connecting",oauth_required:"status.oauthRequired",disconnected:"status.offline"},a=({kind:n,label:r,className:o,onClick:t,title:s})=>e.jsxs("span",{className:d("hub-status",n,t?"cursor-pointer hover:opacity-80":"",o),onClick:t,title:s,children:[e.jsx("span",{className:"hub-dot"}),r!=null&&e.jsx("span",{children:r})]}),j=({status:n,enabled:r,onAuthClick:o,className:t})=>{const{t:s}=u();if(r===!1)return e.jsx(a,{kind:"muted",label:s("server.disable")||"Disabled",className:t});const c=l[n]??"muted",i=n==="oauth_required";return e.jsx(a,{kind:c,label:e.jsxs(e.Fragment,{children:[i&&e.jsx("span",{"aria-hidden":!0,children:"🔐"}),e.jsx("span",{children:s(h[n]||n)})]}),onClick:i&&o?o:void 0,title:i?s("status.clickToAuthorize"):void 0,className:t})};export{j as S,a};
2
+ //# sourceMappingURL=StatusDot-BDBIaafQ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StatusDot-DR803YdX.js","sources":["../../src/components/ui/StatusDot.tsx"],"sourcesContent":["import React from 'react';\nimport { useTranslation } from 'react-i18next';\nimport type { ServerStatus } from '@/types';\nimport { cn } from '@/utils/cn';\n\nexport type DotKind = 'ok' | 'warn' | 'err' | 'muted';\n\nconst STATUS_TO_KIND: Record<ServerStatus, DotKind> = {\n connected: 'ok',\n connecting: 'warn',\n oauth_required: 'warn',\n disconnected: 'err',\n};\n\nconst STATUS_TO_KEY: Record<ServerStatus, string> = {\n connected: 'status.online',\n connecting: 'status.connecting',\n oauth_required: 'status.oauthRequired',\n disconnected: 'status.offline',\n};\n\ninterface StatusDotProps {\n kind: DotKind;\n label?: React.ReactNode;\n className?: string;\n onClick?: (e: React.MouseEvent) => void;\n title?: string;\n}\n\nexport const StatusDot: React.FC<StatusDotProps> = ({ kind, label, className, onClick, title }) => (\n <span\n className={cn('hub-status', kind, onClick ? 'cursor-pointer hover:opacity-80' : '', className)}\n onClick={onClick}\n title={title}\n >\n <span className=\"hub-dot\" />\n {label != null && <span>{label}</span>}\n </span>\n);\n\ninterface ServerStatusDotProps {\n status: ServerStatus;\n enabled?: boolean;\n onAuthClick?: (e: React.MouseEvent) => void;\n className?: string;\n}\n\nexport const ServerStatusDot: React.FC<ServerStatusDotProps> = ({\n status,\n enabled,\n onAuthClick,\n className,\n}) => {\n const { t } = useTranslation();\n if (enabled === false) {\n return <StatusDot kind=\"muted\" label={t('server.disable') || 'Disabled'} className={className} />;\n }\n const kind = STATUS_TO_KIND[status] ?? 'muted';\n const isOAuth = status === 'oauth_required';\n return (\n <StatusDot\n kind={kind}\n label={\n <>\n {isOAuth && <span aria-hidden>🔐</span>}\n <span>{t(STATUS_TO_KEY[status] || status)}</span>\n </>\n }\n onClick={isOAuth && onAuthClick ? onAuthClick : undefined}\n title={isOAuth ? t('status.clickToAuthorize') : undefined}\n className={className}\n />\n );\n};\n"],"names":["STATUS_TO_KIND","STATUS_TO_KEY","StatusDot","kind","label","className","onClick","title","jsxs","cn","jsx","ServerStatusDot","status","enabled","onAuthClick","t","useTranslation","isOAuth","Fragment"],"mappings":"oIAOA,MAAMA,EAAgD,CACpD,UAAW,KACX,WAAY,OACZ,eAAgB,OAChB,aAAc,KAChB,EAEMC,EAA8C,CAClD,UAAW,gBACX,WAAY,oBACZ,eAAgB,uBAChB,aAAc,gBAChB,EAUaC,EAAsC,CAAC,CAAE,KAAAC,EAAM,MAAAC,EAAO,UAAAC,EAAW,QAAAC,EAAS,MAAAC,KACrFC,EAAAA,KAAC,OAAA,CACC,UAAWC,EAAG,aAAcN,EAAMG,EAAU,kCAAoC,GAAID,CAAS,EAC7F,QAAAC,EACA,MAAAC,EAEA,SAAA,CAAAG,EAAAA,IAAC,OAAA,CAAK,UAAU,SAAA,CAAU,EACzBN,GAAS,MAAQM,EAAAA,IAAC,OAAA,CAAM,SAAAN,CAAA,CAAM,CAAA,CAAA,CACjC,EAUWO,EAAkD,CAAC,CAC9D,OAAAC,EACA,QAAAC,EACA,YAAAC,EACA,UAAAT,CACF,IAAM,CACJ,KAAM,CAAE,EAAAU,CAAA,EAAMC,EAAA,EACd,GAAIH,IAAY,GACd,OAAOH,MAACR,GAAU,KAAK,QAAQ,MAAOa,EAAE,gBAAgB,GAAK,WAAY,UAAAV,CAAA,CAAsB,EAEjG,MAAMF,EAAOH,EAAeY,CAAM,GAAK,QACjCK,EAAUL,IAAW,iBAC3B,OACEF,EAAAA,IAACR,EAAA,CACC,KAAAC,EACA,MACEK,EAAAA,KAAAU,WAAA,CACG,SAAA,CAAAD,GAAWP,EAAAA,IAAC,OAAA,CAAK,cAAW,GAAC,SAAA,KAAE,QAC/B,OAAA,CAAM,SAAAK,EAAEd,EAAcW,CAAM,GAAKA,CAAM,CAAA,CAAE,CAAA,EAC5C,EAEF,QAASK,GAAWH,EAAcA,EAAc,OAChD,MAAOG,EAAUF,EAAE,yBAAyB,EAAI,OAChD,UAAAV,CAAA,CAAA,CAGN"}
1
+ {"version":3,"file":"StatusDot-BDBIaafQ.js","sources":["../../src/components/ui/StatusDot.tsx"],"sourcesContent":["import React from 'react';\nimport { useTranslation } from 'react-i18next';\nimport type { ServerStatus } from '@/types';\nimport { cn } from '@/utils/cn';\n\nexport type DotKind = 'ok' | 'warn' | 'err' | 'muted';\n\nconst STATUS_TO_KIND: Record<ServerStatus, DotKind> = {\n connected: 'ok',\n connecting: 'warn',\n oauth_required: 'warn',\n disconnected: 'err',\n};\n\nconst STATUS_TO_KEY: Record<ServerStatus, string> = {\n connected: 'status.online',\n connecting: 'status.connecting',\n oauth_required: 'status.oauthRequired',\n disconnected: 'status.offline',\n};\n\ninterface StatusDotProps {\n kind: DotKind;\n label?: React.ReactNode;\n className?: string;\n onClick?: (e: React.MouseEvent) => void;\n title?: string;\n}\n\nexport const StatusDot: React.FC<StatusDotProps> = ({ kind, label, className, onClick, title }) => (\n <span\n className={cn('hub-status', kind, onClick ? 'cursor-pointer hover:opacity-80' : '', className)}\n onClick={onClick}\n title={title}\n >\n <span className=\"hub-dot\" />\n {label != null && <span>{label}</span>}\n </span>\n);\n\ninterface ServerStatusDotProps {\n status: ServerStatus;\n enabled?: boolean;\n onAuthClick?: (e: React.MouseEvent) => void;\n className?: string;\n}\n\nexport const ServerStatusDot: React.FC<ServerStatusDotProps> = ({\n status,\n enabled,\n onAuthClick,\n className,\n}) => {\n const { t } = useTranslation();\n if (enabled === false) {\n return <StatusDot kind=\"muted\" label={t('server.disable') || 'Disabled'} className={className} />;\n }\n const kind = STATUS_TO_KIND[status] ?? 'muted';\n const isOAuth = status === 'oauth_required';\n return (\n <StatusDot\n kind={kind}\n label={\n <>\n {isOAuth && <span aria-hidden>🔐</span>}\n <span>{t(STATUS_TO_KEY[status] || status)}</span>\n </>\n }\n onClick={isOAuth && onAuthClick ? onAuthClick : undefined}\n title={isOAuth ? t('status.clickToAuthorize') : undefined}\n className={className}\n />\n );\n};\n"],"names":["STATUS_TO_KIND","STATUS_TO_KEY","StatusDot","kind","label","className","onClick","title","jsxs","cn","jsx","ServerStatusDot","status","enabled","onAuthClick","t","useTranslation","isOAuth","Fragment"],"mappings":"oIAOA,MAAMA,EAAgD,CACpD,UAAW,KACX,WAAY,OACZ,eAAgB,OAChB,aAAc,KAChB,EAEMC,EAA8C,CAClD,UAAW,gBACX,WAAY,oBACZ,eAAgB,uBAChB,aAAc,gBAChB,EAUaC,EAAsC,CAAC,CAAE,KAAAC,EAAM,MAAAC,EAAO,UAAAC,EAAW,QAAAC,EAAS,MAAAC,KACrFC,EAAAA,KAAC,OAAA,CACC,UAAWC,EAAG,aAAcN,EAAMG,EAAU,kCAAoC,GAAID,CAAS,EAC7F,QAAAC,EACA,MAAAC,EAEA,SAAA,CAAAG,EAAAA,IAAC,OAAA,CAAK,UAAU,SAAA,CAAU,EACzBN,GAAS,MAAQM,EAAAA,IAAC,OAAA,CAAM,SAAAN,CAAA,CAAM,CAAA,CAAA,CACjC,EAUWO,EAAkD,CAAC,CAC9D,OAAAC,EACA,QAAAC,EACA,YAAAC,EACA,UAAAT,CACF,IAAM,CACJ,KAAM,CAAE,EAAAU,CAAA,EAAMC,EAAA,EACd,GAAIH,IAAY,GACd,OAAOH,MAACR,GAAU,KAAK,QAAQ,MAAOa,EAAE,gBAAgB,GAAK,WAAY,UAAAV,CAAA,CAAsB,EAEjG,MAAMF,EAAOH,EAAeY,CAAM,GAAK,QACjCK,EAAUL,IAAW,iBAC3B,OACEF,EAAAA,IAACR,EAAA,CACC,KAAAC,EACA,MACEK,EAAAA,KAAAU,WAAA,CACG,SAAA,CAAAD,GAAWP,EAAAA,IAAC,OAAA,CAAK,cAAW,GAAC,SAAA,KAAE,QAC/B,OAAA,CAAM,SAAAK,EAAEd,EAAcW,CAAM,GAAKA,CAAM,CAAA,CAAE,CAAA,EAC5C,EAEF,QAASK,GAAWH,EAAcA,EAAc,OAChD,MAAOG,EAAUF,EAAE,yBAAyB,EAAI,OAChD,UAAAV,CAAA,CAAA,CAGN"}
@@ -1,2 +1,2 @@
1
- import{j as s}from"./framework-vendor-BUhDPOUZ.js";import{m as i}from"./index-B9cW2F0H.js";const l=({checked:t,onCheckedChange:n,disabled:o=!1})=>s.jsx("button",{type:"button",role:"switch","aria-checked":t,disabled:o,className:i("relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500",t?"bg-blue-200":"bg-gray-100",o?"opacity-50 cursor-not-allowed":"cursor-pointer"),onClick:()=>!o&&n(!t),children:s.jsx("span",{className:i("inline-block h-4 w-4 transform rounded-full bg-white transition-transform",t?"translate-x-6":"translate-x-1")})});export{l as S};
2
- //# sourceMappingURL=ToggleGroup-DE8t8Ni4.js.map
1
+ import{j as s}from"./framework-vendor-BUhDPOUZ.js";import{m as i}from"./index-C7wNc_3N.js";const l=({checked:t,onCheckedChange:n,disabled:o=!1})=>s.jsx("button",{type:"button",role:"switch","aria-checked":t,disabled:o,className:i("relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500",t?"bg-blue-200":"bg-gray-100",o?"opacity-50 cursor-not-allowed":"cursor-pointer"),onClick:()=>!o&&n(!t),children:s.jsx("span",{className:i("inline-block h-4 w-4 transform rounded-full bg-white transition-transform",t?"translate-x-6":"translate-x-1")})});export{l as S};
2
+ //# sourceMappingURL=ToggleGroup-OOcsllhw.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToggleGroup-DE8t8Ni4.js","sources":["../../src/components/ui/ToggleGroup.tsx"],"sourcesContent":["import React, { ReactNode } from 'react';\nimport { cn } from '@/utils/cn';\n\ninterface ToggleGroupItemProps {\n value: string;\n isSelected: boolean;\n onClick: () => void;\n children: ReactNode;\n}\n\nexport const ToggleGroupItem: React.FC<ToggleGroupItemProps> = ({\n isSelected,\n onClick,\n children\n}) => {\n return (\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={isSelected}\n className={cn(\n \"flex w-full items-center justify-between p-2 rounded transition-colors cursor-pointer\",\n isSelected\n ? \"bg-blue-50 text-blue-700 hover:bg-blue-100 border-l-4 border-blue-500\"\n : \"hover:bg-gray-50 text-gray-700\"\n )}\n onClick={onClick}\n >\n <span className=\"flex items-center\">\n {children}\n </span>\n {isSelected && (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" fill=\"currentColor\" className=\"w-5 h-5 text-blue-500\">\n <path fillRule=\"evenodd\" d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\" clipRule=\"evenodd\" />\n </svg>\n )}\n </button>\n );\n};\n\ninterface ToggleGroupProps {\n label: string;\n helpText?: string;\n noOptionsText?: string;\n values: string[];\n options: { value: string; label: string }[];\n onChange: (values: string[]) => void;\n className?: string;\n}\n\nexport const ToggleGroup: React.FC<ToggleGroupProps> = ({\n label,\n helpText,\n noOptionsText = \"No options available\",\n values,\n options,\n onChange,\n className\n}) => {\n const handleToggle = (value: string) => {\n const isSelected = values.includes(value);\n if (isSelected) {\n onChange(values.filter(v => v !== value));\n } else {\n onChange([...values, value]);\n }\n };\n\n return (\n <div className={className}>\n <label className=\"block text-gray-700 text-sm font-bold mb-2\">\n {label}\n </label>\n <div className=\"border border-gray-200 dark:border-gray-700 rounded shadow max-h-60 overflow-y-auto\">\n {options.length === 0 ? (\n <p className=\"text-gray-500 text-sm p-3\">{noOptionsText}</p>\n ) : (\n <div className=\"space-y-1 p-1\">\n {options.map(option => (\n <ToggleGroupItem\n key={option.value}\n value={option.value}\n isSelected={values.includes(option.value)}\n onClick={() => handleToggle(option.value)}\n >\n {option.label}\n </ToggleGroupItem>\n ))}\n </div>\n )}\n </div>\n {helpText && (\n <p className=\"text-xs text-gray-500 mt-1\">\n {helpText}\n </p>\n )}\n </div>\n );\n};\n\ninterface SwitchProps {\n checked: boolean;\n onCheckedChange: (checked: boolean) => void;\n disabled?: boolean;\n}\n\nexport const Switch: React.FC<SwitchProps> = ({\n checked,\n onCheckedChange,\n disabled = false\n}) => {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled}\n className={cn(\n \"relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500\",\n checked ? \"bg-blue-200\" : \"bg-gray-100\",\n disabled ? \"opacity-50 cursor-not-allowed\" : \"cursor-pointer\"\n )}\n onClick={() => !disabled && onCheckedChange(!checked)}\n >\n <span\n className={cn(\n \"inline-block h-4 w-4 transform rounded-full bg-white transition-transform\",\n checked ? \"translate-x-6\" : \"translate-x-1\"\n )}\n />\n </button>\n );\n};"],"names":["Switch","checked","onCheckedChange","disabled","jsx","cn"],"mappings":"2FA0GO,MAAMA,EAAgC,CAAC,CAC5C,QAAAC,EACA,gBAAAC,EACA,SAAAC,EAAW,EACb,IAEIC,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,KAAK,SACL,eAAcH,EACd,SAAAE,EACA,UAAWE,EACT,wLACAJ,EAAU,cAAgB,cAC1BE,EAAW,gCAAkC,gBAAA,EAE/C,QAAS,IAAM,CAACA,GAAYD,EAAgB,CAACD,CAAO,EAEpD,SAAAG,EAAAA,IAAC,OAAA,CACC,UAAWC,EACT,4EACAJ,EAAU,gBAAkB,eAAA,CAC9B,CAAA,CACF,CAAA"}
1
+ {"version":3,"file":"ToggleGroup-OOcsllhw.js","sources":["../../src/components/ui/ToggleGroup.tsx"],"sourcesContent":["import React, { ReactNode } from 'react';\nimport { cn } from '@/utils/cn';\n\ninterface ToggleGroupItemProps {\n value: string;\n isSelected: boolean;\n onClick: () => void;\n children: ReactNode;\n}\n\nexport const ToggleGroupItem: React.FC<ToggleGroupItemProps> = ({\n isSelected,\n onClick,\n children\n}) => {\n return (\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={isSelected}\n className={cn(\n \"flex w-full items-center justify-between p-2 rounded transition-colors cursor-pointer\",\n isSelected\n ? \"bg-blue-50 text-blue-700 hover:bg-blue-100 border-l-4 border-blue-500\"\n : \"hover:bg-gray-50 text-gray-700\"\n )}\n onClick={onClick}\n >\n <span className=\"flex items-center\">\n {children}\n </span>\n {isSelected && (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" fill=\"currentColor\" className=\"w-5 h-5 text-blue-500\">\n <path fillRule=\"evenodd\" d=\"M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z\" clipRule=\"evenodd\" />\n </svg>\n )}\n </button>\n );\n};\n\ninterface ToggleGroupProps {\n label: string;\n helpText?: string;\n noOptionsText?: string;\n values: string[];\n options: { value: string; label: string }[];\n onChange: (values: string[]) => void;\n className?: string;\n}\n\nexport const ToggleGroup: React.FC<ToggleGroupProps> = ({\n label,\n helpText,\n noOptionsText = \"No options available\",\n values,\n options,\n onChange,\n className\n}) => {\n const handleToggle = (value: string) => {\n const isSelected = values.includes(value);\n if (isSelected) {\n onChange(values.filter(v => v !== value));\n } else {\n onChange([...values, value]);\n }\n };\n\n return (\n <div className={className}>\n <label className=\"block text-gray-700 text-sm font-bold mb-2\">\n {label}\n </label>\n <div className=\"border border-gray-200 dark:border-gray-700 rounded shadow max-h-60 overflow-y-auto\">\n {options.length === 0 ? (\n <p className=\"text-gray-500 text-sm p-3\">{noOptionsText}</p>\n ) : (\n <div className=\"space-y-1 p-1\">\n {options.map(option => (\n <ToggleGroupItem\n key={option.value}\n value={option.value}\n isSelected={values.includes(option.value)}\n onClick={() => handleToggle(option.value)}\n >\n {option.label}\n </ToggleGroupItem>\n ))}\n </div>\n )}\n </div>\n {helpText && (\n <p className=\"text-xs text-gray-500 mt-1\">\n {helpText}\n </p>\n )}\n </div>\n );\n};\n\ninterface SwitchProps {\n checked: boolean;\n onCheckedChange: (checked: boolean) => void;\n disabled?: boolean;\n}\n\nexport const Switch: React.FC<SwitchProps> = ({\n checked,\n onCheckedChange,\n disabled = false\n}) => {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled}\n className={cn(\n \"relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500\",\n checked ? \"bg-blue-200\" : \"bg-gray-100\",\n disabled ? \"opacity-50 cursor-not-allowed\" : \"cursor-pointer\"\n )}\n onClick={() => !disabled && onCheckedChange(!checked)}\n >\n <span\n className={cn(\n \"inline-block h-4 w-4 transform rounded-full bg-white transition-transform\",\n checked ? \"translate-x-6\" : \"translate-x-1\"\n )}\n />\n </button>\n );\n};"],"names":["Switch","checked","onCheckedChange","disabled","jsx","cn"],"mappings":"2FA0GO,MAAMA,EAAgC,CAAC,CAC5C,QAAAC,EACA,gBAAAC,EACA,SAAAC,EAAW,EACb,IAEIC,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,KAAK,SACL,eAAcH,EACd,SAAAE,EACA,UAAWE,EACT,wLACAJ,EAAU,cAAgB,cAC1BE,EAAW,gCAAkC,gBAAA,EAE/C,QAAS,IAAM,CAACA,GAAYD,EAAgB,CAACD,CAAO,EAEpD,SAAAG,EAAAA,IAAC,OAAA,CACC,UAAWC,EACT,4EACAJ,EAAU,gBAAkB,eAAA,CAC9B,CAAA,CACF,CAAA"}
@@ -1,2 +1,2 @@
1
- import{r as d,j as e}from"./framework-vendor-BUhDPOUZ.js";import{j as v,f as k,h as C,e as P,u as S}from"./index-B9cW2F0H.js";import{u as j}from"./i18n-vendor-Kbr87Ofu.js";import{D as A}from"./DeleteDialog-DRbWonMu.js";import{a as E}from"./StatusDot-DR803YdX.js";import{R as U,P as z,j as F,X as D,U as R,t as T,T as L}from"./icons-vendor-CKgJB3SC.js";const y=()=>{const{t:a}=j(),[g,t]=d.useState([]),[o,p]=d.useState(!0),[u,n]=d.useState(null),[m,c]=d.useState(0),i=d.useCallback(async()=>{try{p(!0);const r=await v("/users");if(!r.success){n(r.message||a("users.fetchError"));return}r&&r.success&&Array.isArray(r.data)?t(r.data):(console.error("Invalid user data format:",r),t([])),n(null)}catch(r){console.error("Error fetching users:",r),n(r instanceof Error?r.message:"Failed to fetch users"),t([])}finally{p(!1)}},[]),x=d.useCallback(()=>{c(r=>r+1)},[]),f=async r=>{try{const s=await P("/users",r);return x(),s}catch(s){return n(s instanceof Error?s.message:"Failed to create user"),null}},h=async(r,s)=>{try{const b=await C(`/users/${r}`,s);return x(),b||null}catch(b){return n(b instanceof Error?b.message:"Failed to update user"),null}},l=async r=>{try{const s=await k(`/users/${r}`);return s!=null&&s.success?(x(),s):(n((s==null?void 0:s.message)||a("users.deleteError")),s)}catch(s){return n(s instanceof Error?s.message:"Failed to delete user"),!1}};return d.useEffect(()=>{i()},[i,m]),{users:g,loading:o,error:u,setError:n,triggerRefresh:x,createUser:f,updateUser:h,deleteUser:l}},I=({onAdd:a,onCancel:g})=>{const{t}=j(),{createUser:o}=y(),[p,u]=d.useState(null),[n,m]=d.useState(!1),[c,i]=d.useState({username:"",password:"",isAdmin:!1}),x=async h=>{if(h.preventDefault(),u(null),!c.username.trim()){u(t("users.usernameRequired"));return}if(!c.password.trim()){u(t("users.passwordRequired"));return}if(c.password.length<6){u(t("users.passwordTooShort"));return}m(!0);try{const l=await o(c);l!=null&&l.success?a():u((l==null?void 0:l.message)||t("users.createError"))}catch(l){u(l instanceof Error?l.message:t("users.createError"))}finally{m(!1)}},f=h=>{const{name:l,value:r,type:s,checked:b}=h.target;i(w=>({...w,[l]:s==="checkbox"?b:r}))};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:x,children:[e.jsx("h2",{className:"text-xl font-bold text-gray-900 mb-6",children:t("users.addNew")}),p&&e.jsx("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:p})}),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:[t("users.username")," ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"text",id:"username",name:"username",value:c.username,onChange:f,placeholder:t("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:n})]}),e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"password",className:"block text-sm font-medium text-gray-700 mb-1",children:[t("users.password")," ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"password",id:"password",name:"password",value:c.password,onChange:f,placeholder:t("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:n,minLength:6})]}),e.jsxs("div",{className:"flex items-center pt-2",children:[e.jsx("input",{type:"checkbox",id:"isAdmin",name:"isAdmin",checked:c.isAdmin,onChange:f,className:"h-5 w-5 text-blue-600 focus:ring-blue-500 border-gray-300 rounded transition-colors duration-200",disabled:n}),e.jsx("label",{htmlFor:"isAdmin",className:"ml-3 block text-sm font-medium text-gray-700 cursor-pointer select-none",children:t("users.adminRole")})]})]}),e.jsxs("div",{className:"flex justify-end space-x-2 mt-6",children:[e.jsx("button",{type:"button",onClick:g,className:"hub-btn",disabled:n,children:t("common.cancel")}),e.jsxs("button",{type:"submit",className:"hub-btn primary",disabled:n,children:[n&&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"})]}),t(n?"common.creating":"users.create")]})]})]})})})},q=({user:a,onEdit:g,onCancel:t})=>{const{t:o}=j(),{updateUser:p}=y(),[u,n]=d.useState(null),[m,c]=d.useState(!1),[i,x]=d.useState({isAdmin:a.isAdmin,newPassword:"",confirmPassword:""}),f=async l=>{if(l.preventDefault(),n(null),i.newPassword&&i.newPassword!==i.confirmPassword){n(o("users.passwordMismatch"));return}if(i.newPassword&&i.newPassword.length<6){n(o("users.passwordTooShort"));return}c(!0);try{const r={isAdmin:i.isAdmin};i.newPassword&&(r.newPassword=i.newPassword);const s=await p(a.username,r);s!=null&&s.success?g():n((s==null?void 0:s.message)||o("users.updateError"))}catch(r){n(r instanceof Error?r.message:o("users.updateError"))}finally{c(!1)}},h=l=>{const{name:r,value:s,type:b,checked:w}=l.target;x(N=>({...N,[r]:b==="checkbox"?w:s}))};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:f,children:[e.jsxs("h2",{className:"text-xl font-bold text-gray-900 mb-6",children:[o("users.edit")," - ",e.jsx("span",{className:"text-blue-600",children:a.username})]}),u&&e.jsx("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:u})}),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:i.isAdmin,onChange:h,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:o("users.adminRole")})]}),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:o("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:o("users.newPassword")}),e.jsx("input",{type:"password",id:"newPassword",name:"newPassword",value:i.newPassword,onChange:h,placeholder:o("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:m,minLength:6})]}),i.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:o("users.confirmPassword")}),e.jsx("input",{type:"password",id:"confirmPassword",name:"confirmPassword",value:i.confirmPassword,onChange:h,placeholder:o("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:m,minLength:6})]})]})]})]}),e.jsxs("div",{className:"flex justify-end space-x-2 mt-6",children:[e.jsx("button",{type:"button",onClick:t,className:"hub-btn",disabled:m,children:o("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"})]}),o(m?"common.updating":"users.update")]})]})]})})})},V=()=>{const{t:a}=j(),{auth:g}=S(),t=g.user,{users:o,loading:p,error:u,setError:n,deleteUser:m,triggerRefresh:c}=y(),[i,x]=d.useState(null),[f,h]=d.useState(!1),[l,r]=d.useState(null);return t!=null&&t.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:a("pages.users.title")}),e.jsxs("p",{className:"hub-sub",children:[e.jsx("span",{className:"hub-num",children:o.length})," ",a("nav.users").toLowerCase()]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>c(),"aria-label":a("common.refresh"),children:[e.jsx(U,{size:13})," ",a("common.refresh")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>h(!0),children:[e.jsx(z,{size:13})," ",a("users.add")]})]})]}),u&&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(F,{size:14,className:"flex-shrink-0"}),e.jsx("span",{className:"truncate text-[13px]",children:u})]}),e.jsx("button",{className:"hub-icon-btn sm",onClick:()=>n(null),children:e.jsx(D,{size:13})})]}),p?e.jsx("div",{className:"hub-card p-10 text-center",style:{color:"var(--hub-ink-3)"},children:a("app.loading")}):o.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(R,{size:18})}),e.jsx("div",{className:"font-medium",style:{color:"var(--hub-ink-2)",fontSize:13},children:a("users.noUsers")}),e.jsx("button",{onClick:()=>h(!0),className:"hub-btn ghost sm",style:{color:"var(--hub-accent)"},children:a("users.addFirst")})]})}):e.jsxs("div",{className:"hub-card overflow-hidden",children:[e.jsxs("div",{className:"hub-row head hub-mono",style:{gridTemplateColumns:"1.6fr 100px 100px"},children:[e.jsx("div",{children:a("users.username")}),e.jsx("div",{children:a("users.role")}),e.jsx("div",{className:"text-right",children:a("users.actions")})]}),o.map(s=>{const b=(t==null?void 0:t.username)===s.username;return e.jsxs("div",{className:"hub-row hover",style:{gridTemplateColumns:"1.6fr 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}),b&&e.jsx("span",{className:"hub-tag accent",style:{fontSize:10},children:a("users.currentUser")})]}),e.jsx("div",{children:e.jsx(E,{kind:s.isAdmin?"ok":"muted",label:s.isAdmin?a("users.admin"):a("users.user")})}),e.jsxs("div",{className:"flex justify-end gap-1",children:[e.jsx("button",{onClick:()=>x(s),className:"hub-icon-btn sm",title:a("users.edit"),children:e.jsx(T,{size:13})}),!b&&e.jsx("button",{onClick:()=>r(s.username),className:"hub-icon-btn sm",title:a("users.delete"),style:{color:"var(--hub-err)"},children:e.jsx(L,{size:13})})]})]},s.username)})]}),f&&e.jsx(I,{onAdd:()=>{h(!1),c()},onCancel:()=>h(!1)}),i&&e.jsx(q,{user:i,onEdit:()=>{x(null),c()},onCancel:()=>x(null)}),e.jsx(A,{isOpen:!!l,onClose:()=>r(null),onConfirm:async()=>{if(l){const s=await m(l);s!=null&&s.success||n((s==null?void 0:s.message)||a("users.deleteError")),r(null)}},serverName:l||"",isGroup:!1,isUser:!0})]}):e.jsx("div",{className:"hub-card p-6 text-center",style:{color:"var(--hub-err)"},children:a("users.adminRequired")})};export{V as default};
2
- //# sourceMappingURL=UsersPage-CMscqAmn.js.map
1
+ import{r as d,j as e}from"./framework-vendor-BUhDPOUZ.js";import{j as v,f as k,h as C,e as P,u as S}from"./index-C7wNc_3N.js";import{u as j}from"./i18n-vendor-Kbr87Ofu.js";import{D as A}from"./DeleteDialog-DRbWonMu.js";import{a as E}from"./StatusDot-BDBIaafQ.js";import{R as U,P as z,j as F,X as D,U as R,t as T,T as L}from"./icons-vendor-CKgJB3SC.js";const y=()=>{const{t:a}=j(),[g,t]=d.useState([]),[o,p]=d.useState(!0),[u,n]=d.useState(null),[m,c]=d.useState(0),i=d.useCallback(async()=>{try{p(!0);const r=await v("/users");if(!r.success){n(r.message||a("users.fetchError"));return}r&&r.success&&Array.isArray(r.data)?t(r.data):(console.error("Invalid user data format:",r),t([])),n(null)}catch(r){console.error("Error fetching users:",r),n(r instanceof Error?r.message:"Failed to fetch users"),t([])}finally{p(!1)}},[]),x=d.useCallback(()=>{c(r=>r+1)},[]),f=async r=>{try{const s=await P("/users",r);return x(),s}catch(s){return n(s instanceof Error?s.message:"Failed to create user"),null}},h=async(r,s)=>{try{const b=await C(`/users/${r}`,s);return x(),b||null}catch(b){return n(b instanceof Error?b.message:"Failed to update user"),null}},l=async r=>{try{const s=await k(`/users/${r}`);return s!=null&&s.success?(x(),s):(n((s==null?void 0:s.message)||a("users.deleteError")),s)}catch(s){return n(s instanceof Error?s.message:"Failed to delete user"),!1}};return d.useEffect(()=>{i()},[i,m]),{users:g,loading:o,error:u,setError:n,triggerRefresh:x,createUser:f,updateUser:h,deleteUser:l}},I=({onAdd:a,onCancel:g})=>{const{t}=j(),{createUser:o}=y(),[p,u]=d.useState(null),[n,m]=d.useState(!1),[c,i]=d.useState({username:"",password:"",isAdmin:!1}),x=async h=>{if(h.preventDefault(),u(null),!c.username.trim()){u(t("users.usernameRequired"));return}if(!c.password.trim()){u(t("users.passwordRequired"));return}if(c.password.length<6){u(t("users.passwordTooShort"));return}m(!0);try{const l=await o(c);l!=null&&l.success?a():u((l==null?void 0:l.message)||t("users.createError"))}catch(l){u(l instanceof Error?l.message:t("users.createError"))}finally{m(!1)}},f=h=>{const{name:l,value:r,type:s,checked:b}=h.target;i(w=>({...w,[l]:s==="checkbox"?b:r}))};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:x,children:[e.jsx("h2",{className:"text-xl font-bold text-gray-900 mb-6",children:t("users.addNew")}),p&&e.jsx("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:p})}),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:[t("users.username")," ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"text",id:"username",name:"username",value:c.username,onChange:f,placeholder:t("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:n})]}),e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"password",className:"block text-sm font-medium text-gray-700 mb-1",children:[t("users.password")," ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"password",id:"password",name:"password",value:c.password,onChange:f,placeholder:t("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:n,minLength:6})]}),e.jsxs("div",{className:"flex items-center pt-2",children:[e.jsx("input",{type:"checkbox",id:"isAdmin",name:"isAdmin",checked:c.isAdmin,onChange:f,className:"h-5 w-5 text-blue-600 focus:ring-blue-500 border-gray-300 rounded transition-colors duration-200",disabled:n}),e.jsx("label",{htmlFor:"isAdmin",className:"ml-3 block text-sm font-medium text-gray-700 cursor-pointer select-none",children:t("users.adminRole")})]})]}),e.jsxs("div",{className:"flex justify-end space-x-2 mt-6",children:[e.jsx("button",{type:"button",onClick:g,className:"hub-btn",disabled:n,children:t("common.cancel")}),e.jsxs("button",{type:"submit",className:"hub-btn primary",disabled:n,children:[n&&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"})]}),t(n?"common.creating":"users.create")]})]})]})})})},q=({user:a,onEdit:g,onCancel:t})=>{const{t:o}=j(),{updateUser:p}=y(),[u,n]=d.useState(null),[m,c]=d.useState(!1),[i,x]=d.useState({isAdmin:a.isAdmin,newPassword:"",confirmPassword:""}),f=async l=>{if(l.preventDefault(),n(null),i.newPassword&&i.newPassword!==i.confirmPassword){n(o("users.passwordMismatch"));return}if(i.newPassword&&i.newPassword.length<6){n(o("users.passwordTooShort"));return}c(!0);try{const r={isAdmin:i.isAdmin};i.newPassword&&(r.newPassword=i.newPassword);const s=await p(a.username,r);s!=null&&s.success?g():n((s==null?void 0:s.message)||o("users.updateError"))}catch(r){n(r instanceof Error?r.message:o("users.updateError"))}finally{c(!1)}},h=l=>{const{name:r,value:s,type:b,checked:w}=l.target;x(N=>({...N,[r]:b==="checkbox"?w:s}))};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:f,children:[e.jsxs("h2",{className:"text-xl font-bold text-gray-900 mb-6",children:[o("users.edit")," - ",e.jsx("span",{className:"text-blue-600",children:a.username})]}),u&&e.jsx("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:u})}),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:i.isAdmin,onChange:h,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:o("users.adminRole")})]}),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:o("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:o("users.newPassword")}),e.jsx("input",{type:"password",id:"newPassword",name:"newPassword",value:i.newPassword,onChange:h,placeholder:o("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:m,minLength:6})]}),i.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:o("users.confirmPassword")}),e.jsx("input",{type:"password",id:"confirmPassword",name:"confirmPassword",value:i.confirmPassword,onChange:h,placeholder:o("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:m,minLength:6})]})]})]})]}),e.jsxs("div",{className:"flex justify-end space-x-2 mt-6",children:[e.jsx("button",{type:"button",onClick:t,className:"hub-btn",disabled:m,children:o("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"})]}),o(m?"common.updating":"users.update")]})]})]})})})},V=()=>{const{t:a}=j(),{auth:g}=S(),t=g.user,{users:o,loading:p,error:u,setError:n,deleteUser:m,triggerRefresh:c}=y(),[i,x]=d.useState(null),[f,h]=d.useState(!1),[l,r]=d.useState(null);return t!=null&&t.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:a("pages.users.title")}),e.jsxs("p",{className:"hub-sub",children:[e.jsx("span",{className:"hub-num",children:o.length})," ",a("nav.users").toLowerCase()]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>c(),"aria-label":a("common.refresh"),children:[e.jsx(U,{size:13})," ",a("common.refresh")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>h(!0),children:[e.jsx(z,{size:13})," ",a("users.add")]})]})]}),u&&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(F,{size:14,className:"flex-shrink-0"}),e.jsx("span",{className:"truncate text-[13px]",children:u})]}),e.jsx("button",{className:"hub-icon-btn sm",onClick:()=>n(null),children:e.jsx(D,{size:13})})]}),p?e.jsx("div",{className:"hub-card p-10 text-center",style:{color:"var(--hub-ink-3)"},children:a("app.loading")}):o.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(R,{size:18})}),e.jsx("div",{className:"font-medium",style:{color:"var(--hub-ink-2)",fontSize:13},children:a("users.noUsers")}),e.jsx("button",{onClick:()=>h(!0),className:"hub-btn ghost sm",style:{color:"var(--hub-accent)"},children:a("users.addFirst")})]})}):e.jsxs("div",{className:"hub-card overflow-hidden",children:[e.jsxs("div",{className:"hub-row head hub-mono",style:{gridTemplateColumns:"1.6fr 100px 100px"},children:[e.jsx("div",{children:a("users.username")}),e.jsx("div",{children:a("users.role")}),e.jsx("div",{className:"text-right",children:a("users.actions")})]}),o.map(s=>{const b=(t==null?void 0:t.username)===s.username;return e.jsxs("div",{className:"hub-row hover",style:{gridTemplateColumns:"1.6fr 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}),b&&e.jsx("span",{className:"hub-tag accent",style:{fontSize:10},children:a("users.currentUser")})]}),e.jsx("div",{children:e.jsx(E,{kind:s.isAdmin?"ok":"muted",label:s.isAdmin?a("users.admin"):a("users.user")})}),e.jsxs("div",{className:"flex justify-end gap-1",children:[e.jsx("button",{onClick:()=>x(s),className:"hub-icon-btn sm",title:a("users.edit"),children:e.jsx(T,{size:13})}),!b&&e.jsx("button",{onClick:()=>r(s.username),className:"hub-icon-btn sm",title:a("users.delete"),style:{color:"var(--hub-err)"},children:e.jsx(L,{size:13})})]})]},s.username)})]}),f&&e.jsx(I,{onAdd:()=>{h(!1),c()},onCancel:()=>h(!1)}),i&&e.jsx(q,{user:i,onEdit:()=>{x(null),c()},onCancel:()=>x(null)}),e.jsx(A,{isOpen:!!l,onClose:()=>r(null),onConfirm:async()=>{if(l){const s=await m(l);s!=null&&s.success||n((s==null?void 0:s.message)||a("users.deleteError")),r(null)}},serverName:l||"",isGroup:!1,isUser:!0})]}):e.jsx("div",{className:"hub-card p-6 text-center",style:{color:"var(--hub-err)"},children:a("users.adminRequired")})};export{V as default};
2
+ //# sourceMappingURL=UsersPage-DL8E7KtW.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UsersPage-CMscqAmn.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';\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 [isSubmitting, setIsSubmitting] = useState(false);\n\n const [formData, setFormData] = useState<UserFormData>({\n username: '',\n password: '',\n isAdmin: false,\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\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 if (formData.password.length < 6) {\n setError(t('users.passwordTooShort'));\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n const result = await createUser(formData);\n if (result?.success) {\n onAdd();\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 </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=\"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={6}\n />\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';\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 [isSubmitting, setIsSubmitting] = useState(false);\n\n const [formData, setFormData] = useState({\n isAdmin: user.isAdmin,\n newPassword: '',\n confirmPassword: '',\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\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 && formData.newPassword.length < 6) {\n setError(t('users.passwordTooShort'));\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n const updateData: UserUpdateData = {\n isAdmin: formData.isAdmin,\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 {\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 </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 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={6}\n />\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={6}\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 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 { Edit3, Trash2, User as UserIcon, Plus, AlertCircle, X, RefreshCw } 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 100px 100px' }}\n >\n <div>{t('users.username')}</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 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>\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 <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","t","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","isSubmitting","setIsSubmitting","formData","setFormData","handleSubmit","e","handleInputChange","name","value","type","checked","jsx","jsxs","EditUserForm","user","onEdit","updateData","UsersPage","auth","useAuth","currentUser","usersLoading","userError","setUserError","editingUser","setEditingUser","showAddForm","setShowAddForm","userToDelete","setUserToDelete","RefreshCw","Plus","AlertCircle","X","UserIcon","isCurrentUser","StatusDot","Edit3","Trash2","DeleteDialog"],"mappings":"gWAKO,MAAMA,EAAc,IAAM,CAC/B,KAAM,CAAE,EAAAC,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,SAAWb,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,UAAWpB,EAAE,mBAAmB,CAAC,EAC3CoB,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,ECzFMG,EAAc,CAAC,CAAE,MAAAC,EAAO,SAAAC,KAAiC,CAC7D,KAAM,CAAE,CAAA,EAAM7B,EAAA,EACR,CAAE,WAAAiB,CAAA,EAAenB,EAAA,EACjB,CAACQ,EAAOC,CAAQ,EAAIJ,EAAAA,SAAwB,IAAI,EAChD,CAAC2B,EAAcC,CAAe,EAAI5B,EAAAA,SAAS,EAAK,EAEhD,CAAC6B,EAAUC,CAAW,EAAI9B,WAAuB,CACrD,SAAU,GACV,SAAU,GACV,QAAS,EAAA,CACV,EAEK+B,EAAe,MAAOC,GAAuB,CAIjD,GAHAA,EAAE,eAAA,EACF5B,EAAS,IAAI,EAET,CAACyB,EAAS,SAAS,OAAQ,CAC7BzB,EAAS,EAAE,wBAAwB,CAAC,EACpC,MACF,CAEA,GAAI,CAACyB,EAAS,SAAS,OAAQ,CAC7BzB,EAAS,EAAE,wBAAwB,CAAC,EACpC,MACF,CAEA,GAAIyB,EAAS,SAAS,OAAS,EAAG,CAChCzB,EAAS,EAAE,wBAAwB,CAAC,EACpC,MACF,CAEAwB,EAAgB,EAAI,EAEpB,GAAI,CACF,MAAMZ,EAAS,MAAMF,EAAWe,CAAQ,EACpCb,GAAA,MAAAA,EAAQ,QACVS,EAAA,EAEArB,GAASY,GAAA,YAAAA,EAAQ,UAAW,EAAE,mBAAmB,CAAC,CAEtD,OAASL,EAAK,CACZP,EAASO,aAAe,MAAQA,EAAI,QAAU,EAAE,mBAAmB,CAAC,CACtE,QAAA,CACEiB,EAAgB,EAAK,CACvB,CACF,EAEMK,EAAqBD,GAA2C,CACpE,KAAM,CAAE,KAAAE,EAAM,MAAAC,EAAO,KAAAC,EAAM,QAAAC,CAAA,EAAYL,EAAE,OACzCF,EAAajB,IAAU,CACrB,GAAGA,EACH,CAACqB,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,SAAUR,EACd,SAAA,CAAAO,MAAC,KAAA,CAAG,UAAU,uCAAwC,SAAA,EAAE,cAAc,EAAE,EAEvEnC,GACCmC,EAAAA,IAAC,MAAA,CAAI,UAAU,uEACb,eAAC,IAAA,CAAE,UAAU,sBAAuB,SAAAnC,CAAA,CAAM,CAAA,CAC5C,EAGFoC,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAA,EAAAA,KAAC,QAAA,CAAM,QAAQ,WAAW,UAAU,+CACjC,SAAA,CAAA,EAAE,gBAAgB,EAAE,IAACD,EAAAA,IAAC,OAAA,CAAK,UAAU,eAAe,SAAA,GAAA,CAAC,CAAA,EACxD,EACAA,EAAAA,IAAC,QAAA,CACC,KAAK,OACL,GAAG,WACH,KAAK,WACL,MAAOT,EAAS,SAChB,SAAUI,EACV,YAAa,EAAE,2BAA2B,EAC1C,UAAU,yKACV,SAAQ,GACR,SAAUN,CAAA,CAAA,CACZ,EACF,SAEC,MAAA,CACC,SAAA,CAAAY,EAAAA,KAAC,QAAA,CAAM,QAAQ,WAAW,UAAU,+CACjC,SAAA,CAAA,EAAE,gBAAgB,EAAE,IAACD,EAAAA,IAAC,OAAA,CAAK,UAAU,eAAe,SAAA,GAAA,CAAC,CAAA,EACxD,EACAA,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,GAAG,WACH,KAAK,WACL,MAAOT,EAAS,SAChB,SAAUI,EACV,YAAa,EAAE,2BAA2B,EAC1C,UAAU,yKACV,SAAQ,GACR,SAAUN,EACV,UAAW,CAAA,CAAA,CACb,EACF,EAEAY,EAAAA,KAAC,MAAA,CAAI,UAAU,yBACb,SAAA,CAAAD,EAAAA,IAAC,QAAA,CACC,KAAK,WACL,GAAG,UACH,KAAK,UACL,QAAST,EAAS,QAClB,SAAUI,EACV,UAAU,mGACV,SAAUN,CAAA,CAAA,EAEZW,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,QAASZ,EACT,UAAU,UACV,SAAUC,EAET,WAAE,eAAe,CAAA,CAAA,EAEpBY,EAAAA,KAAC,SAAA,CACC,KAAK,SACL,UAAU,kBACV,SAAUZ,EAET,SAAA,CAAAA,GACCY,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,EAGW,EAAfX,EAAiB,kBAAuB,cAAN,CAAoB,CAAA,CAAA,CACzD,CAAA,CACF,CAAA,CAAA,CACF,EACF,EACF,CAEJ,ECpKMa,EAAe,CAAC,CAAE,KAAAC,EAAM,OAAAC,EAAQ,SAAAhB,KAAkC,CACtE,KAAM,CAAE,EAAA9B,CAAA,EAAMC,EAAA,EACR,CAAE,WAAAqB,CAAA,EAAevB,EAAA,EACjB,CAACQ,EAAOC,CAAQ,EAAIJ,EAAAA,SAAwB,IAAI,EAChD,CAAC2B,EAAcC,CAAe,EAAI5B,EAAAA,SAAS,EAAK,EAEhD,CAAC6B,EAAUC,CAAW,EAAI9B,WAAS,CACvC,QAASyC,EAAK,QACd,YAAa,GACb,gBAAiB,EAAA,CAClB,EAEKV,EAAe,MAAOC,GAAuB,CAKjD,GAJAA,EAAE,eAAA,EACF5B,EAAS,IAAI,EAGTyB,EAAS,aAAeA,EAAS,cAAgBA,EAAS,gBAAiB,CAC7EzB,EAASR,EAAE,wBAAwB,CAAC,EACpC,MACF,CAEA,GAAIiC,EAAS,aAAeA,EAAS,YAAY,OAAS,EAAG,CAC3DzB,EAASR,EAAE,wBAAwB,CAAC,EACpC,MACF,CAEAgC,EAAgB,EAAI,EAEpB,GAAI,CACF,MAAMe,EAA6B,CACjC,QAASd,EAAS,OAAA,EAGhBA,EAAS,cACXc,EAAW,YAAcd,EAAS,aAGpC,MAAMb,EAAS,MAAME,EAAWuB,EAAK,SAAUE,CAAU,EACrD3B,GAAA,MAAAA,EAAQ,QACV0B,EAAA,EAEAtC,GAASY,GAAA,YAAAA,EAAQ,UAAWpB,EAAE,mBAAmB,CAAC,CAEtD,OAASe,EAAK,CACZP,EAASO,aAAe,MAAQA,EAAI,QAAUf,EAAE,mBAAmB,CAAC,CACtE,QAAA,CACEgC,EAAgB,EAAK,CACvB,CACF,EAEMK,EAAqBD,GAA2C,CACpE,KAAM,CAAE,KAAAE,EAAM,MAAAC,EAAO,KAAAC,EAAM,QAAAC,CAAA,EAAYL,EAAE,OACzCF,EAAajB,IAAU,CACrB,GAAGA,EACH,CAACqB,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,SAAUR,EACd,SAAA,CAAAQ,EAAAA,KAAC,KAAA,CAAG,UAAU,uCACX,SAAA,CAAA3C,EAAE,YAAY,EAAE,MAAG0C,EAAAA,IAAC,OAAA,CAAK,UAAU,gBAAiB,WAAK,QAAA,CAAS,CAAA,EACrE,EAECnC,GACCmC,EAAAA,IAAC,MAAA,CAAI,UAAU,uEACb,eAAC,IAAA,CAAE,UAAU,sBAAuB,SAAAnC,CAAA,CAAM,CAAA,CAC5C,EAGFoC,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,QAAST,EAAS,QAClB,SAAUI,EACV,UAAU,mGACV,SAAUN,CAAA,CAAA,EAEZW,EAAAA,IAAC,QAAA,CACC,QAAQ,UACR,UAAU,0EAET,WAAE,iBAAiB,CAAA,CAAA,CACtB,EACF,EAEAC,EAAAA,KAAC,MAAA,CAAI,UAAU,0DACb,SAAA,CAAAD,MAAC,IAAA,CAAE,UAAU,oEACV,SAAA1C,EAAE,sBAAsB,EAC3B,EAEA2C,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,MAAOT,EAAS,YAChB,SAAUI,EACV,YAAarC,EAAE,8BAA8B,EAC7C,UAAU,yKACV,SAAU+B,EACV,UAAW,CAAA,CAAA,CACb,EACF,EAECE,EAAS,aACRU,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,MAAOT,EAAS,gBAChB,SAAUI,EACV,YAAarC,EAAE,kCAAkC,EACjD,UAAU,yKACV,SAAU+B,EACV,UAAW,CAAA,CAAA,CACb,CAAA,CACF,CAAA,CAAA,CAEJ,CAAA,CAAA,CACF,CAAA,EACF,EAEAY,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAD,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,QAASZ,EACT,UAAU,UACV,SAAUC,EAET,WAAE,eAAe,CAAA,CAAA,EAEpBY,EAAAA,KAAC,SAAA,CACC,KAAK,SACL,UAAU,kBACV,SAAUZ,EAET,SAAA,CAAAA,GACCY,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,EAGW1C,EAAf+B,EAAiB,kBAAuB,cAAN,CAAoB,CAAA,CAAA,CACzD,CAAA,CACF,CAAA,CAAA,CACF,EACF,EACF,CAEJ,EC1LMiB,EAAsB,IAAM,CAChC,KAAM,CAAE,EAAAhD,CAAA,EAAMC,EAAA,EACR,CAAE,KAAAgD,CAAA,EAASC,EAAA,EACXC,EAAcF,EAAK,KACnB,CACJ,MAAA/C,EACA,QAASkD,EACT,MAAOC,EACP,SAAUC,EACV,WAAA7B,EACA,eAAAT,CAAA,EACEjB,EAAA,EAEE,CAACwD,EAAaC,CAAc,EAAIpD,EAAAA,SAAsB,IAAI,EAC1D,CAACqD,EAAaC,CAAc,EAAItD,EAAAA,SAAS,EAAK,EAC9C,CAACuD,EAAcC,CAAe,EAAIxD,EAAAA,SAAwB,IAAI,EAEpE,OAAK+C,GAAA,MAAAA,EAAa,eASf,MAAA,CACC,SAAA,CAAAR,EAAAA,KAAC,MAAA,CAAI,UAAU,4CACb,SAAA,CAAAA,OAAC,MAAA,CACC,SAAA,CAAAD,MAAC,KAAA,CAAG,UAAU,SAAU,SAAA1C,EAAE,mBAAmB,EAAE,EAC/C2C,EAAAA,KAAC,IAAA,CAAE,UAAU,UACX,SAAA,CAAAD,EAAAA,IAAC,OAAA,CAAK,UAAU,UAAW,SAAAxC,EAAM,OAAO,EAAO,IAAEF,EAAE,WAAW,EAAE,YAAA,CAAY,CAAA,CAC9E,CAAA,EACF,EACA2C,EAAAA,KAAC,MAAA,CAAI,UAAU,aACb,SAAA,CAAAA,EAAAA,KAAC,SAAA,CACC,UAAU,UACV,QAAS,IAAM3B,EAAA,EACf,aAAYhB,EAAE,gBAAgB,EAE9B,SAAA,CAAA0C,EAAAA,IAACmB,EAAA,CAAU,KAAM,EAAA,CAAI,EAAE,IAAE7D,EAAE,gBAAgB,CAAA,CAAA,CAAA,EAE7C2C,OAAC,UAAO,UAAU,kBAAkB,QAAS,IAAMe,EAAe,EAAI,EACpE,SAAA,CAAAhB,EAAAA,IAACoB,EAAA,CAAK,KAAM,EAAA,CAAI,EAAE,IAAE9D,EAAE,WAAW,CAAA,CAAA,CACnC,CAAA,CAAA,CACF,CAAA,EACF,EAECqD,GACCV,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,IAACqB,EAAA,CAAY,KAAM,GAAI,UAAU,gBAAgB,EACjDrB,EAAAA,IAAC,OAAA,CAAK,UAAU,uBAAwB,SAAAW,CAAA,CAAU,CAAA,EACpD,EACAX,EAAAA,IAAC,SAAA,CAAO,UAAU,kBAAkB,QAAS,IAAMY,EAAa,IAAI,EAClE,SAAAZ,EAAAA,IAACsB,EAAA,CAAE,KAAM,GAAI,CAAA,CACf,CAAA,CAAA,CAAA,EAIHZ,EACCV,EAAAA,IAAC,MAAA,CAAI,UAAU,4BAA4B,MAAO,CAAE,MAAO,kBAAA,EACxD,SAAA1C,EAAE,aAAa,CAAA,CAClB,EACEE,EAAM,SAAW,EACnBwC,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,IAACuB,EAAA,CAAS,KAAM,EAAA,CAAI,CAAA,CAAA,EAEtBvB,EAAAA,IAAC,MAAA,CAAI,UAAU,cAAc,MAAO,CAAE,MAAO,mBAAoB,SAAU,EAAA,EACxE,SAAA1C,EAAE,eAAe,CAAA,CACpB,EACA0C,EAAAA,IAAC,SAAA,CACC,QAAS,IAAMgB,EAAe,EAAI,EAClC,UAAU,mBACV,MAAO,CAAE,MAAO,mBAAA,EAEf,WAAE,gBAAgB,CAAA,CAAA,CACrB,CAAA,CACF,CAAA,CACF,EAEAf,EAAAA,KAAC,MAAA,CAAI,UAAU,2BACb,SAAA,CAAAA,EAAAA,KAAC,MAAA,CACC,UAAU,wBACV,MAAO,CAAE,oBAAqB,mBAAA,EAE9B,SAAA,CAAAD,EAAAA,IAAC,MAAA,CAAK,SAAA1C,EAAE,gBAAgB,CAAA,CAAE,EAC1B0C,EAAAA,IAAC,MAAA,CAAK,SAAA1C,EAAE,YAAY,CAAA,CAAE,QACrB,MAAA,CAAI,UAAU,aAAc,SAAAA,EAAE,eAAe,CAAA,CAAE,CAAA,CAAA,CAAA,EAEjDE,EAAM,IAAK2C,GAAS,CACnB,MAAMqB,GAAgBf,GAAA,YAAAA,EAAa,YAAaN,EAAK,SACrD,OACEF,EAAAA,KAAC,MAAA,CAEC,UAAU,gBACV,MAAO,CAAE,oBAAqB,mBAAA,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,SAAAG,EAAK,SAAS,OAAO,CAAC,EAAE,YAAA,CAAY,CAAA,EAEvCH,EAAAA,IAAC,OAAA,CACC,UAAU,oBACV,MAAO,CAAE,SAAU,GAAI,MAAO,gBAAA,EAE7B,SAAAG,EAAK,QAAA,CAAA,EAEPqB,GACCxB,EAAAA,IAAC,OAAA,CAAK,UAAU,iBAAiB,MAAO,CAAE,SAAU,EAAA,EACjD,SAAA1C,EAAE,mBAAmB,CAAA,CACxB,CAAA,EAEJ,QACC,MAAA,CACC,SAAA0C,EAAAA,IAACyB,EAAA,CACC,KAAMtB,EAAK,QAAU,KAAO,QAC5B,MAAOA,EAAK,QAAU7C,EAAE,aAAa,EAAIA,EAAE,YAAY,CAAA,CAAA,EAE3D,EACA2C,EAAAA,KAAC,MAAA,CAAI,UAAU,yBACb,SAAA,CAAAD,EAAAA,IAAC,SAAA,CACC,QAAS,IAAMc,EAAeX,CAAI,EAClC,UAAU,kBACV,MAAO7C,EAAE,YAAY,EAErB,SAAA0C,EAAAA,IAAC0B,EAAA,CAAM,KAAM,EAAA,CAAI,CAAA,CAAA,EAElB,CAACF,GACAxB,EAAAA,IAAC,SAAA,CACC,QAAS,IAAMkB,EAAgBf,EAAK,QAAQ,EAC5C,UAAU,kBACV,MAAO7C,EAAE,cAAc,EACvB,MAAO,CAAE,MAAO,gBAAA,EAEhB,SAAA0C,EAAAA,IAAC2B,EAAA,CAAO,KAAM,EAAA,CAAI,CAAA,CAAA,CACpB,CAAA,CAEJ,CAAA,CAAA,EAxDKxB,EAAK,QAAA,CA2DhB,CAAC,CAAA,EACH,EAGDY,GACCf,EAAAA,IAACd,EAAA,CACC,MAAO,IAAM,CACX8B,EAAe,EAAK,EACpB1C,EAAA,CACF,EACA,SAAU,IAAM0C,EAAe,EAAK,CAAA,CAAA,EAIvCH,GACCb,EAAAA,IAACE,EAAA,CACC,KAAMW,EACN,OAAQ,IAAM,CACZC,EAAe,IAAI,EACnBxC,EAAA,CACF,EACA,SAAU,IAAMwC,EAAe,IAAI,CAAA,CAAA,EAIvCd,EAAAA,IAAC4B,EAAA,CACC,OAAQ,CAAC,CAACX,EACV,QAAS,IAAMC,EAAgB,IAAI,EACnC,UAAW,SAAY,CACrB,GAAID,EAAc,CAChB,MAAMvC,EAAS,MAAMK,EAAWkC,CAAY,EACvCvC,GAAA,MAAAA,EAAQ,SACXkC,GAAalC,GAAA,YAAAA,EAAQ,UAAWpB,EAAE,mBAAmB,CAAC,EAExD4D,EAAgB,IAAI,CACtB,CACF,EACA,WAAYD,GAAgB,GAC5B,QAAS,GACT,OAAQ,EAAA,CAAA,CACV,EACF,EAlMEjB,EAAAA,IAAC,MAAA,CAAI,UAAU,2BAA2B,MAAO,CAAE,MAAO,gBAAA,EACvD,SAAA1C,EAAE,qBAAqB,CAAA,CAC1B,CAkMN"}
1
+ {"version":3,"file":"UsersPage-DL8E7KtW.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';\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 [isSubmitting, setIsSubmitting] = useState(false);\n\n const [formData, setFormData] = useState<UserFormData>({\n username: '',\n password: '',\n isAdmin: false,\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\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 if (formData.password.length < 6) {\n setError(t('users.passwordTooShort'));\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n const result = await createUser(formData);\n if (result?.success) {\n onAdd();\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 </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=\"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={6}\n />\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';\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 [isSubmitting, setIsSubmitting] = useState(false);\n\n const [formData, setFormData] = useState({\n isAdmin: user.isAdmin,\n newPassword: '',\n confirmPassword: '',\n });\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\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 && formData.newPassword.length < 6) {\n setError(t('users.passwordTooShort'));\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n const updateData: UserUpdateData = {\n isAdmin: formData.isAdmin,\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 {\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 </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 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={6}\n />\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={6}\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 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 { Edit3, Trash2, User as UserIcon, Plus, AlertCircle, X, RefreshCw } 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 100px 100px' }}\n >\n <div>{t('users.username')}</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 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>\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 <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","t","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","isSubmitting","setIsSubmitting","formData","setFormData","handleSubmit","e","handleInputChange","name","value","type","checked","jsx","jsxs","EditUserForm","user","onEdit","updateData","UsersPage","auth","useAuth","currentUser","usersLoading","userError","setUserError","editingUser","setEditingUser","showAddForm","setShowAddForm","userToDelete","setUserToDelete","RefreshCw","Plus","AlertCircle","X","UserIcon","isCurrentUser","StatusDot","Edit3","Trash2","DeleteDialog"],"mappings":"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"}