@udhuong/admin-core 0.1.0 → 0.1.1

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 (275) hide show
  1. package/dist/client/auth/AuthContext.d.ts +19 -0
  2. package/dist/client/auth/AuthContext.d.ts.map +1 -0
  3. package/dist/client/auth/AuthContext.js +115 -0
  4. package/dist/client/auth/AuthContext.js.map +1 -0
  5. package/dist/client/auth/PermissionGuard.d.ts +12 -0
  6. package/dist/client/auth/PermissionGuard.d.ts.map +1 -0
  7. package/dist/client/auth/PermissionGuard.js +23 -0
  8. package/dist/client/auth/PermissionGuard.js.map +1 -0
  9. package/dist/client/auth/ProtectedPage.d.ts +9 -0
  10. package/dist/client/auth/ProtectedPage.d.ts.map +1 -0
  11. package/dist/client/auth/ProtectedPage.js +57 -0
  12. package/dist/client/auth/ProtectedPage.js.map +1 -0
  13. package/dist/client/auth/ProtectedRoute.d.ts +4 -0
  14. package/dist/client/auth/ProtectedRoute.d.ts.map +1 -0
  15. package/dist/client/auth/ProtectedRoute.js +25 -0
  16. package/dist/client/auth/ProtectedRoute.js.map +1 -0
  17. package/dist/client/auth/api-client.d.ts +4 -0
  18. package/dist/client/auth/api-client.d.ts.map +1 -0
  19. package/dist/client/auth/api-client.js +83 -0
  20. package/dist/client/auth/api-client.js.map +1 -0
  21. package/dist/client/auth/auth.service.d.ts +7 -0
  22. package/dist/client/auth/auth.service.d.ts.map +1 -0
  23. package/dist/client/auth/auth.service.js +19 -0
  24. package/dist/client/auth/auth.service.js.map +1 -0
  25. package/dist/client/auth/index.d.ts +7 -0
  26. package/dist/client/auth/index.d.ts.map +1 -0
  27. package/dist/client/auth/index.js +7 -0
  28. package/dist/client/auth/index.js.map +1 -0
  29. package/dist/client/components/PageHeader.d.ts +19 -0
  30. package/dist/client/components/PageHeader.d.ts.map +1 -0
  31. package/dist/client/components/PageHeader.js +7 -0
  32. package/dist/client/components/PageHeader.js.map +1 -0
  33. package/dist/client/components/Providers.d.ts +9 -0
  34. package/dist/client/components/Providers.d.ts.map +1 -0
  35. package/dist/client/components/Providers.js +52 -0
  36. package/dist/client/components/Providers.js.map +1 -0
  37. package/dist/client/components/ThemeToggle.d.ts +6 -0
  38. package/dist/client/components/ThemeToggle.d.ts.map +1 -0
  39. package/dist/client/components/ThemeToggle.js +22 -0
  40. package/dist/client/components/ThemeToggle.js.map +1 -0
  41. package/dist/client/components/index.d.ts +4 -0
  42. package/dist/client/components/index.d.ts.map +1 -0
  43. package/dist/client/components/index.js +4 -0
  44. package/dist/client/components/index.js.map +1 -0
  45. package/dist/client/config/AdminConfigContext.d.ts +12 -0
  46. package/dist/client/config/AdminConfigContext.d.ts.map +1 -0
  47. package/dist/client/config/AdminConfigContext.js +29 -0
  48. package/dist/client/config/AdminConfigContext.js.map +1 -0
  49. package/dist/client/config/defaults.d.ts +4 -0
  50. package/dist/client/config/defaults.d.ts.map +1 -0
  51. package/dist/client/config/defaults.js +86 -0
  52. package/dist/client/config/defaults.js.map +1 -0
  53. package/dist/client/config/types.d.ts +61 -0
  54. package/dist/client/config/types.d.ts.map +1 -0
  55. package/dist/client/config/types.js +2 -0
  56. package/dist/client/config/types.js.map +1 -0
  57. package/dist/client/constants/api-routes.d.ts +104 -0
  58. package/dist/client/constants/api-routes.d.ts.map +1 -0
  59. package/dist/client/constants/api-routes.js +91 -0
  60. package/dist/client/constants/api-routes.js.map +1 -0
  61. package/dist/client/constants/permissions.d.ts +38 -0
  62. package/dist/client/constants/permissions.d.ts.map +1 -0
  63. package/dist/client/constants/permissions.js +37 -0
  64. package/dist/client/constants/permissions.js.map +1 -0
  65. package/dist/client/contexts/ThemeContext.d.ts +3 -0
  66. package/dist/client/contexts/ThemeContext.d.ts.map +1 -0
  67. package/dist/client/contexts/ThemeContext.js +8 -0
  68. package/dist/client/contexts/ThemeContext.js.map +1 -0
  69. package/dist/client/hooks/useToast.d.ts +14 -0
  70. package/dist/client/hooks/useToast.d.ts.map +1 -0
  71. package/dist/client/hooks/useToast.js +90 -0
  72. package/dist/client/hooks/useToast.js.map +1 -0
  73. package/dist/client/index.d.ts +25 -0
  74. package/dist/client/index.d.ts.map +1 -0
  75. package/dist/client/index.js +33 -0
  76. package/dist/client/index.js.map +1 -0
  77. package/dist/client/layouts/AdminLayout.d.ts +4 -0
  78. package/dist/client/layouts/AdminLayout.d.ts.map +1 -0
  79. package/dist/client/layouts/AdminLayout.js +72 -0
  80. package/dist/client/layouts/AdminLayout.js.map +1 -0
  81. package/dist/client/layouts/Sidebar.d.ts +6 -0
  82. package/dist/client/layouts/Sidebar.d.ts.map +1 -0
  83. package/dist/client/layouts/Sidebar.js +86 -0
  84. package/dist/client/layouts/Sidebar.js.map +1 -0
  85. package/dist/client/layouts/index.d.ts +3 -0
  86. package/dist/client/layouts/index.d.ts.map +1 -0
  87. package/dist/client/layouts/index.js +3 -0
  88. package/dist/client/layouts/index.js.map +1 -0
  89. package/dist/client/lib/utils.d.ts +5 -0
  90. package/dist/client/lib/utils.d.ts.map +1 -0
  91. package/dist/client/lib/utils.js +22 -0
  92. package/dist/client/lib/utils.js.map +1 -0
  93. package/dist/client/pages/ChangePasswordPage.d.ts +2 -0
  94. package/dist/client/pages/ChangePasswordPage.d.ts.map +1 -0
  95. package/dist/client/pages/ChangePasswordPage.js +58 -0
  96. package/dist/client/pages/ChangePasswordPage.js.map +1 -0
  97. package/dist/client/pages/ForgotPasswordPage.d.ts +2 -0
  98. package/dist/client/pages/ForgotPasswordPage.d.ts.map +1 -0
  99. package/dist/client/pages/ForgotPasswordPage.js +30 -0
  100. package/dist/client/pages/ForgotPasswordPage.js.map +1 -0
  101. package/dist/client/pages/LoginPage.d.ts +2 -0
  102. package/dist/client/pages/LoginPage.d.ts.map +1 -0
  103. package/dist/client/pages/LoginPage.js +52 -0
  104. package/dist/client/pages/LoginPage.js.map +1 -0
  105. package/dist/client/pages/PermissionsPage.d.ts +2 -0
  106. package/dist/client/pages/PermissionsPage.d.ts.map +1 -0
  107. package/dist/client/pages/PermissionsPage.js +42 -0
  108. package/dist/client/pages/PermissionsPage.js.map +1 -0
  109. package/dist/client/pages/ProfilePage.d.ts +2 -0
  110. package/dist/client/pages/ProfilePage.d.ts.map +1 -0
  111. package/dist/client/pages/ProfilePage.js +107 -0
  112. package/dist/client/pages/ProfilePage.js.map +1 -0
  113. package/dist/client/pages/ResetPasswordPage.d.ts +2 -0
  114. package/dist/client/pages/ResetPasswordPage.d.ts.map +1 -0
  115. package/dist/client/pages/ResetPasswordPage.js +68 -0
  116. package/dist/client/pages/ResetPasswordPage.js.map +1 -0
  117. package/dist/client/pages/RolesPage.d.ts +2 -0
  118. package/dist/client/pages/RolesPage.d.ts.map +1 -0
  119. package/dist/client/pages/RolesPage.js +82 -0
  120. package/dist/client/pages/RolesPage.js.map +1 -0
  121. package/dist/client/pages/UnauthorizedPage.d.ts +2 -0
  122. package/dist/client/pages/UnauthorizedPage.d.ts.map +1 -0
  123. package/dist/client/pages/UnauthorizedPage.js +9 -0
  124. package/dist/client/pages/UnauthorizedPage.js.map +1 -0
  125. package/dist/client/pages/UserDetailPage.d.ts +6 -0
  126. package/dist/client/pages/UserDetailPage.d.ts.map +1 -0
  127. package/dist/client/pages/UserDetailPage.js +220 -0
  128. package/dist/client/pages/UserDetailPage.js.map +1 -0
  129. package/dist/client/pages/UserPreferencesPage.d.ts +2 -0
  130. package/dist/client/pages/UserPreferencesPage.d.ts.map +1 -0
  131. package/dist/client/pages/UserPreferencesPage.js +152 -0
  132. package/dist/client/pages/UserPreferencesPage.js.map +1 -0
  133. package/dist/client/pages/UsersPage.d.ts +2 -0
  134. package/dist/client/pages/UsersPage.d.ts.map +1 -0
  135. package/dist/client/pages/UsersPage.js +228 -0
  136. package/dist/client/pages/UsersPage.js.map +1 -0
  137. package/dist/client/pages/VerifyEmailPage.d.ts +2 -0
  138. package/dist/client/pages/VerifyEmailPage.d.ts.map +1 -0
  139. package/dist/client/pages/VerifyEmailPage.js +43 -0
  140. package/dist/client/pages/VerifyEmailPage.js.map +1 -0
  141. package/dist/client/pages/index.d.ts +13 -0
  142. package/dist/client/pages/index.d.ts.map +1 -0
  143. package/dist/client/pages/index.js +13 -0
  144. package/dist/client/pages/index.js.map +1 -0
  145. package/dist/client/pages/roles/RoleModal.d.ts +10 -0
  146. package/dist/client/pages/roles/RoleModal.d.ts.map +1 -0
  147. package/dist/client/pages/roles/RoleModal.js +123 -0
  148. package/dist/client/pages/roles/RoleModal.js.map +1 -0
  149. package/dist/client/pages/users/DangerZone.d.ts +10 -0
  150. package/dist/client/pages/users/DangerZone.d.ts.map +1 -0
  151. package/dist/client/pages/users/DangerZone.js +8 -0
  152. package/dist/client/pages/users/DangerZone.js.map +1 -0
  153. package/dist/client/pages/users/ProfileCard.d.ts +14 -0
  154. package/dist/client/pages/users/ProfileCard.d.ts.map +1 -0
  155. package/dist/client/pages/users/ProfileCard.js +8 -0
  156. package/dist/client/pages/users/ProfileCard.js.map +1 -0
  157. package/dist/client/pages/users/SecuritySection.d.ts +14 -0
  158. package/dist/client/pages/users/SecuritySection.d.ts.map +1 -0
  159. package/dist/client/pages/users/SecuritySection.js +16 -0
  160. package/dist/client/pages/users/SecuritySection.js.map +1 -0
  161. package/dist/client/pages/users/SessionsSection.d.ts +25 -0
  162. package/dist/client/pages/users/SessionsSection.d.ts.map +1 -0
  163. package/dist/client/pages/users/SessionsSection.js +93 -0
  164. package/dist/client/pages/users/SessionsSection.js.map +1 -0
  165. package/dist/client/pages/users/SocialAccountsSection.d.ts +29 -0
  166. package/dist/client/pages/users/SocialAccountsSection.d.ts.map +1 -0
  167. package/dist/client/pages/users/SocialAccountsSection.js +72 -0
  168. package/dist/client/pages/users/SocialAccountsSection.js.map +1 -0
  169. package/dist/client/pages/users/UserDetailModal.d.ts +24 -0
  170. package/dist/client/pages/users/UserDetailModal.d.ts.map +1 -0
  171. package/dist/client/pages/users/UserDetailModal.js +42 -0
  172. package/dist/client/pages/users/UserDetailModal.js.map +1 -0
  173. package/dist/client/pages/users/UserFilters.d.ts +14 -0
  174. package/dist/client/pages/users/UserFilters.d.ts.map +1 -0
  175. package/dist/client/pages/users/UserFilters.js +22 -0
  176. package/dist/client/pages/users/UserFilters.js.map +1 -0
  177. package/dist/client/pages/users/UserStats.d.ts +9 -0
  178. package/dist/client/pages/users/UserStats.d.ts.map +1 -0
  179. package/dist/client/pages/users/UserStats.js +37 -0
  180. package/dist/client/pages/users/UserStats.js.map +1 -0
  181. package/dist/client/pages/users/UserTable.d.ts +24 -0
  182. package/dist/client/pages/users/UserTable.d.ts.map +1 -0
  183. package/dist/client/pages/users/UserTable.js +39 -0
  184. package/dist/client/pages/users/UserTable.js.map +1 -0
  185. package/dist/client/pages/users/VerificationSection.d.ts +9 -0
  186. package/dist/client/pages/users/VerificationSection.d.ts.map +1 -0
  187. package/dist/client/pages/users/VerificationSection.js +8 -0
  188. package/dist/client/pages/users/VerificationSection.js.map +1 -0
  189. package/dist/client/types/auth.d.ts +29 -0
  190. package/dist/client/types/auth.d.ts.map +1 -0
  191. package/dist/client/types/auth.js +2 -0
  192. package/dist/client/types/auth.js.map +1 -0
  193. package/dist/client/types/rbac.d.ts +39 -0
  194. package/dist/client/types/rbac.d.ts.map +1 -0
  195. package/dist/client/types/rbac.js +5 -0
  196. package/dist/client/types/rbac.js.map +1 -0
  197. package/dist/client/types/status.d.ts +12 -0
  198. package/dist/client/types/status.d.ts.map +1 -0
  199. package/dist/client/types/status.js +14 -0
  200. package/dist/client/types/status.js.map +1 -0
  201. package/dist/client/ui/Badge.d.ts +14 -0
  202. package/dist/client/ui/Badge.d.ts.map +1 -0
  203. package/dist/client/ui/Badge.js +32 -0
  204. package/dist/client/ui/Badge.js.map +1 -0
  205. package/dist/client/ui/Button.d.ts +18 -0
  206. package/dist/client/ui/Button.d.ts.map +1 -0
  207. package/dist/client/ui/Button.js +33 -0
  208. package/dist/client/ui/Button.js.map +1 -0
  209. package/dist/client/ui/Checkbox.d.ts +14 -0
  210. package/dist/client/ui/Checkbox.d.ts.map +1 -0
  211. package/dist/client/ui/Checkbox.js +35 -0
  212. package/dist/client/ui/Checkbox.js.map +1 -0
  213. package/dist/client/ui/ConfirmModal.d.ts +14 -0
  214. package/dist/client/ui/ConfirmModal.d.ts.map +1 -0
  215. package/dist/client/ui/ConfirmModal.js +43 -0
  216. package/dist/client/ui/ConfirmModal.js.map +1 -0
  217. package/dist/client/ui/DataTable.d.ts +69 -0
  218. package/dist/client/ui/DataTable.d.ts.map +1 -0
  219. package/dist/client/ui/DataTable.js +133 -0
  220. package/dist/client/ui/DataTable.js.map +1 -0
  221. package/dist/client/ui/Dropdown.d.ts +11 -0
  222. package/dist/client/ui/Dropdown.d.ts.map +1 -0
  223. package/dist/client/ui/Dropdown.js +65 -0
  224. package/dist/client/ui/Dropdown.js.map +1 -0
  225. package/dist/client/ui/FileInput.d.ts +16 -0
  226. package/dist/client/ui/FileInput.d.ts.map +1 -0
  227. package/dist/client/ui/FileInput.js +45 -0
  228. package/dist/client/ui/FileInput.js.map +1 -0
  229. package/dist/client/ui/Input.d.ts +16 -0
  230. package/dist/client/ui/Input.d.ts.map +1 -0
  231. package/dist/client/ui/Input.js +33 -0
  232. package/dist/client/ui/Input.js.map +1 -0
  233. package/dist/client/ui/LoadingSpinner.d.ts +7 -0
  234. package/dist/client/ui/LoadingSpinner.d.ts.map +1 -0
  235. package/dist/client/ui/LoadingSpinner.js +12 -0
  236. package/dist/client/ui/LoadingSpinner.js.map +1 -0
  237. package/dist/client/ui/Modal.d.ts +14 -0
  238. package/dist/client/ui/Modal.d.ts.map +1 -0
  239. package/dist/client/ui/Modal.js +46 -0
  240. package/dist/client/ui/Modal.js.map +1 -0
  241. package/dist/client/ui/Pagination.d.ts +14 -0
  242. package/dist/client/ui/Pagination.d.ts.map +1 -0
  243. package/dist/client/ui/Pagination.js +50 -0
  244. package/dist/client/ui/Pagination.js.map +1 -0
  245. package/dist/client/ui/Radio.d.ts +34 -0
  246. package/dist/client/ui/Radio.d.ts.map +1 -0
  247. package/dist/client/ui/Radio.js +44 -0
  248. package/dist/client/ui/Radio.js.map +1 -0
  249. package/dist/client/ui/Range.d.ts +17 -0
  250. package/dist/client/ui/Range.d.ts.map +1 -0
  251. package/dist/client/ui/Range.js +28 -0
  252. package/dist/client/ui/Range.js.map +1 -0
  253. package/dist/client/ui/Rating.d.ts +18 -0
  254. package/dist/client/ui/Rating.d.ts.map +1 -0
  255. package/dist/client/ui/Rating.js +64 -0
  256. package/dist/client/ui/Rating.js.map +1 -0
  257. package/dist/client/ui/Select.d.ts +20 -0
  258. package/dist/client/ui/Select.d.ts.map +1 -0
  259. package/dist/client/ui/Select.js +30 -0
  260. package/dist/client/ui/Select.js.map +1 -0
  261. package/dist/client/ui/Textarea.d.ts +14 -0
  262. package/dist/client/ui/Textarea.d.ts.map +1 -0
  263. package/dist/client/ui/Textarea.js +20 -0
  264. package/dist/client/ui/Textarea.js.map +1 -0
  265. package/dist/client/ui/Toggle.d.ts +13 -0
  266. package/dist/client/ui/Toggle.d.ts.map +1 -0
  267. package/dist/client/ui/Toggle.js +29 -0
  268. package/dist/client/ui/Toggle.js.map +1 -0
  269. package/dist/client/ui/index.d.ts +31 -0
  270. package/dist/client/ui/index.d.ts.map +1 -0
  271. package/dist/client/ui/index.js +35 -0
  272. package/dist/client/ui/index.js.map +1 -0
  273. package/dist/tailwind.preset.d.ts +10 -0
  274. package/dist/tailwind.preset.js +12 -0
  275. package/package.json +46 -17
@@ -0,0 +1,14 @@
1
+ interface SecuritySectionProps {
2
+ isEditing: boolean;
3
+ passwordMethod: 'SET_PASSWORD' | 'SEND_LINK';
4
+ onPasswordMethodChange: (method: 'SET_PASSWORD' | 'SEND_LINK') => void;
5
+ newPassword: string;
6
+ onNewPasswordChange: (value: string) => void;
7
+ onResetPassword: () => void;
8
+ onGeneratePassword: () => void;
9
+ twoFactorEnabled?: boolean;
10
+ onDisable2FA: () => void;
11
+ }
12
+ export declare const SecuritySection: ({ isEditing, passwordMethod, onPasswordMethodChange, newPassword, onNewPasswordChange, onResetPassword, onGeneratePassword, twoFactorEnabled, onDisable2FA, }: SecuritySectionProps) => import("react/jsx-runtime").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=SecuritySection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecuritySection.d.ts","sourceRoot":"","sources":["../../../../src/client/pages/users/SecuritySection.tsx"],"names":[],"mappings":"AAKA,UAAU,oBAAoB;IAC5B,SAAS,EAAE,OAAO,CAAA;IAClB,cAAc,EAAE,cAAc,GAAG,WAAW,CAAA;IAC5C,sBAAsB,EAAE,CAAC,MAAM,EAAE,cAAc,GAAG,WAAW,KAAK,IAAI,CAAA;IACtE,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5C,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,kBAAkB,EAAE,MAAM,IAAI,CAAA;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB;AAED,eAAO,MAAM,eAAe,GAAI,+JAU7B,oBAAoB,4CA4JtB,CAAA"}
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { Key, Eye, EyeOff, RefreshCw, Lock, Unlock, Shield } from 'lucide-react';
5
+ export const SecuritySection = ({ isEditing, passwordMethod, onPasswordMethodChange, newPassword, onNewPasswordChange, onResetPassword, onGeneratePassword, twoFactorEnabled, onDisable2FA, }) => {
6
+ const [showPassword, setShowPassword] = useState(false);
7
+ // Use the passed password prop for both edit and create modes
8
+ const currentPassword = newPassword;
9
+ // const handlePasswordChange = isEditing ? onNewPasswordChange : setLocalPassword
10
+ return (_jsxs("div", { className: "bg-white rounded-2xl border border-gray-200 overflow-hidden", children: [_jsx("div", { className: "p-6 border-b border-gray-100 bg-gray-50/50", children: _jsxs("h3", { className: "font-semibold text-gray-900 flex items-center gap-2", children: [_jsx(Shield, { className: "w-5 h-5 text-gray-500" }), "B\u1EA3o m\u1EADt & \u0110\u0103ng nh\u1EADp"] }) }), _jsxs("div", { className: "p-6 space-y-6", children: [isEditing ? (
11
+ /* Edit Mode: Reset Password Options */
12
+ _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex gap-6", children: [_jsxs("label", { className: "flex items-center gap-2.5 cursor-pointer group", children: [_jsx("div", { className: `w-5 h-5 rounded-full border flex items-center justify-center transition-all ${passwordMethod === 'SET_PASSWORD' ? 'border-primary' : 'border-gray-300 group-hover:border-primary/50'}`, children: passwordMethod === 'SET_PASSWORD' && _jsx("div", { className: "w-2.5 h-2.5 rounded-full bg-primary" }) }), _jsx("input", { type: "radio", checked: passwordMethod === 'SET_PASSWORD', onChange: () => onPasswordMethodChange('SET_PASSWORD'), className: "hidden" }), _jsx("span", { className: "font-medium text-gray-700", children: "\u0110\u1EB7t m\u1EADt kh\u1EA9u m\u1EDBi" })] }), _jsxs("label", { className: "flex items-center gap-2.5 cursor-pointer group", children: [_jsx("div", { className: `w-5 h-5 rounded-full border flex items-center justify-center transition-all ${passwordMethod === 'SEND_LINK' ? 'border-primary' : 'border-gray-300 group-hover:border-primary/50'}`, children: passwordMethod === 'SEND_LINK' && _jsx("div", { className: "w-2.5 h-2.5 rounded-full bg-primary" }) }), _jsx("input", { type: "radio", checked: passwordMethod === 'SEND_LINK', onChange: () => onPasswordMethodChange('SEND_LINK'), className: "hidden" }), _jsx("span", { className: "font-medium text-gray-700", children: "G\u1EEDi link qua email" })] })] }), passwordMethod === 'SET_PASSWORD' && (_jsxs("div", { className: "flex gap-2", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(Key, { className: "absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" }), _jsx("input", { type: "text", value: newPassword, onChange: (e) => onNewPasswordChange(e.target.value), placeholder: "Nh\u1EADp m\u1EADt kh\u1EA9u m\u1EDBi...", className: "w-full pl-10 pr-4 py-2.5 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all" })] }), _jsx("button", { type: "button", onClick: onResetPassword, disabled: !newPassword, className: "px-5 py-2.5 bg-primary text-white rounded-xl hover:bg-primary/90 font-medium disabled:opacity-50 disabled:cursor-not-allowed", children: "L\u01B0u" })] })), passwordMethod === 'SEND_LINK' && (_jsx("button", { type: "button", onClick: onResetPassword, className: "w-full sm:w-auto px-6 py-2.5 bg-primary text-white rounded-xl hover:bg-primary/90 font-medium", children: "G\u1EEDi link reset password ngay" })), _jsx("div", { className: "pt-4 border-t border-gray-50", children: _jsxs("button", { type: "button", onClick: onGeneratePassword, className: "text-sm font-medium text-primary hover:text-primary/80 flex items-center gap-1.5 transition-colors", children: [_jsx(RefreshCw, { className: "w-3.5 h-3.5" }), "T\u1EA1o m\u1EADt kh\u1EA9u ng\u1EABu nhi\u00EAn an to\u00E0n"] }) })] })) : (
13
+ /* Create Mode: Initial Password */
14
+ _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-gray-500 mb-1.5 uppercase", children: "M\u1EADt kh\u1EA9u kh\u1EDFi t\u1EA1o" }), _jsxs("div", { className: "flex gap-2", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(Key, { className: "absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" }), _jsx("input", { type: showPassword ? 'text' : 'password', required: true, value: currentPassword, onChange: (e) => onNewPasswordChange(e.target.value), className: "w-full pl-10 pr-10 py-2.5 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all", placeholder: "M\u1EADt kh\u1EA9u \u00EDt nh\u1EA5t 6 k\u00FD t\u1EF1" }), _jsx("button", { type: "button", onClick: () => setShowPassword(!showPassword), className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600", children: showPassword ? _jsx(EyeOff, { className: "w-4 h-4" }) : _jsx(Eye, { className: "w-4 h-4" }) })] }), _jsxs("button", { type: "button", onClick: onGeneratePassword, className: "px-4 py-2.5 bg-gray-50 text-gray-700 border border-gray-200 rounded-xl hover:bg-gray-100 font-medium flex items-center gap-2", children: [_jsx(RefreshCw, { className: "w-4 h-4" }), _jsx("span", { className: "hidden sm:inline", children: "Random" })] })] })] })), isEditing && twoFactorEnabled !== undefined && (_jsx("div", { className: "pt-6 border-t border-gray-100", children: _jsxs("div", { className: "flex items-center justify-between p-4 bg-gray-50 rounded-xl border border-gray-100", children: [_jsxs("div", { children: [_jsxs("h4", { className: "font-medium text-gray-900 flex items-center gap-2", children: [twoFactorEnabled ? _jsx(Lock, { className: "w-4 h-4 text-purple-600" }) : _jsx(Unlock, { className: "w-4 h-4 text-gray-400" }), "X\u00E1c th\u1EF1c 2 b\u01B0\u1EDBc (2FA)"] }), _jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: twoFactorEnabled ? 'Người dùng đang bật bảo mật 2 lớp.' : 'Người dùng chưa kích hoạt bảo mật 2 lớp.' })] }), twoFactorEnabled && (_jsx("button", { type: "button", onClick: onDisable2FA, className: "px-4 py-2 bg-white border border-gray-200 text-red-600 rounded-lg hover:bg-red-50 hover:border-red-100 text-sm font-medium transition-colors", children: "T\u1EAFt 2FA" }))] }) }))] })] }));
15
+ };
16
+ //# sourceMappingURL=SecuritySection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecuritySection.js","sourceRoot":"","sources":["../../../../src/client/pages/users/SecuritySection.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ interface UserSession {
2
+ id: number;
3
+ userId: number;
4
+ sessionToken: string;
5
+ deviceName: string;
6
+ deviceFingerprint?: string;
7
+ ipAddress: string;
8
+ userAgent?: string;
9
+ clientId?: number;
10
+ isCurrent: boolean;
11
+ expiresAt: string;
12
+ lastActiveAt?: string;
13
+ revokedAt?: string;
14
+ revokedReason?: string;
15
+ createdAt: string;
16
+ updatedAt: string;
17
+ }
18
+ interface SessionsSectionProps {
19
+ userId: string;
20
+ sessions: UserSession[];
21
+ onRefresh: () => void;
22
+ }
23
+ export declare function SessionsSection({ userId, sessions, onRefresh }: SessionsSectionProps): import("react/jsx-runtime").JSX.Element;
24
+ export {};
25
+ //# sourceMappingURL=SessionsSection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionsSection.d.ts","sourceRoot":"","sources":["../../../../src/client/pages/users/SessionsSection.tsx"],"names":[],"mappings":"AAQA,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,UAAU,oBAAoB;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,oBAAoB,2CA4KpF"}
@@ -0,0 +1,93 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { Smartphone, Monitor, Globe, Clock, Shield, Trash2 } from 'lucide-react';
5
+ import toast from 'react-hot-toast';
6
+ import { getApiClient } from '../../auth/api-client';
7
+ import { CORE_API_ROUTES } from '../../constants/api-routes';
8
+ export function SessionsSection({ userId, sessions, onRefresh }) {
9
+ const apiClient = getApiClient();
10
+ const [revoking, setRevoking] = useState(null);
11
+ const [revokingAll, setRevokingAll] = useState(false);
12
+ const handleRevokeSession = async (sessionId) => {
13
+ if (!confirm('Bạn có chắc chắn muốn thu hồi session này?'))
14
+ return;
15
+ setRevoking(sessionId);
16
+ try {
17
+ await apiClient.delete(CORE_API_ROUTES.ADMIN_USERS.REVOKE_SESSION(userId, sessionId));
18
+ toast.success('Đã thu hồi session');
19
+ onRefresh();
20
+ }
21
+ catch (error) {
22
+ toast.error(error.response?.data?.message || 'Không thể thu hồi session');
23
+ }
24
+ finally {
25
+ setRevoking(null);
26
+ }
27
+ };
28
+ const handleRevokeAllSessions = async () => {
29
+ if (!confirm('Bạn có chắc chắn muốn thu hồi TẤT CẢ các session của user này?'))
30
+ return;
31
+ setRevokingAll(true);
32
+ try {
33
+ const response = await apiClient.post(CORE_API_ROUTES.ADMIN_USERS.REVOKE_ALL_SESSIONS(userId));
34
+ toast.success(response.data?.message || 'Đã thu hồi tất cả sessions');
35
+ onRefresh();
36
+ }
37
+ catch (error) {
38
+ toast.error(error.response?.data?.message || 'Không thể thu hồi sessions');
39
+ }
40
+ finally {
41
+ setRevokingAll(false);
42
+ }
43
+ };
44
+ const getDeviceIcon = (deviceName, userAgent) => {
45
+ const ua = (userAgent || deviceName || '').toLowerCase();
46
+ if (ua.includes('mobile') || ua.includes('android') || ua.includes('iphone')) {
47
+ return _jsx(Smartphone, { className: "w-5 h-5" });
48
+ }
49
+ return _jsx(Monitor, { className: "w-5 h-5" });
50
+ };
51
+ const getBrowserInfo = (userAgent) => {
52
+ if (!userAgent)
53
+ return 'Unknown Browser';
54
+ const ua = userAgent.toLowerCase();
55
+ if (ua.includes('chrome'))
56
+ return 'Chrome';
57
+ if (ua.includes('firefox'))
58
+ return 'Firefox';
59
+ if (ua.includes('safari'))
60
+ return 'Safari';
61
+ if (ua.includes('edge'))
62
+ return 'Edge';
63
+ return 'Unknown Browser';
64
+ };
65
+ const formatDate = (date) => {
66
+ return new Date(date).toLocaleString('vi-VN', {
67
+ year: 'numeric',
68
+ month: '2-digit',
69
+ day: '2-digit',
70
+ hour: '2-digit',
71
+ minute: '2-digit',
72
+ });
73
+ };
74
+ const formatRelativeTime = (date) => {
75
+ const now = new Date();
76
+ const then = new Date(date);
77
+ const diffMs = now.getTime() - then.getTime();
78
+ const diffMins = Math.floor(diffMs / 60000);
79
+ const diffHours = Math.floor(diffMins / 60);
80
+ const diffDays = Math.floor(diffHours / 24);
81
+ if (diffMins < 1)
82
+ return 'Vừa xong';
83
+ if (diffMins < 60)
84
+ return `${diffMins} phút trước`;
85
+ if (diffHours < 24)
86
+ return `${diffHours} giờ trước`;
87
+ if (diffDays < 7)
88
+ return `${diffDays} ngày trước`;
89
+ return formatDate(date);
90
+ };
91
+ return (_jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-200", children: [_jsx("div", { className: "p-6 border-b border-gray-200", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center", children: _jsx(Shield, { className: "w-5 h-5 text-blue-600" }) }), _jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Phi\u00EAn \u0111\u0103ng nh\u1EADp" }), _jsxs("p", { className: "text-sm text-gray-500", children: [sessions.length, " phi\u00EAn \u0111ang ho\u1EA1t \u0111\u1ED9ng"] })] })] }), sessions.length > 0 && (_jsx("button", { onClick: handleRevokeAllSessions, disabled: revokingAll, className: "px-4 py-2 text-sm font-medium text-red-600 hover:bg-red-50 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: revokingAll ? 'Đang thu hồi...' : 'Thu hồi tất cả' }))] }) }), _jsx("div", { className: "divide-y divide-gray-200", children: sessions.length === 0 ? (_jsxs("div", { className: "p-12 text-center", children: [_jsx(Shield, { className: "w-12 h-12 text-gray-300 mx-auto mb-3" }), _jsx("p", { className: "text-gray-500", children: "Kh\u00F4ng c\u00F3 phi\u00EAn \u0111\u0103ng nh\u1EADp n\u00E0o" })] })) : (sessions.map((session) => (_jsx("div", { className: "p-6 hover:bg-gray-50 transition-colors", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-start gap-4 flex-1", children: [_jsx("div", { className: "w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center flex-shrink-0", children: getDeviceIcon(session.deviceName, session.userAgent) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2 mb-1", children: [_jsx("h3", { className: "text-base font-semibold text-gray-900", children: session.deviceName || 'Unknown Device' }), session.isCurrent && (_jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-green-100 text-green-700 rounded-full", children: "Hi\u1EC7n t\u1EA1i" }))] }), _jsxs("div", { className: "space-y-1.5 mt-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: [_jsx(Globe, { className: "w-4 h-4" }), _jsx("span", { children: session.ipAddress }), _jsx("span", { className: "text-gray-400", children: "\u2022" }), _jsx("span", { children: getBrowserInfo(session.userAgent) })] }), session.lastActiveAt && (_jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: [_jsx(Clock, { className: "w-4 h-4" }), _jsxs("span", { children: ["Ho\u1EA1t \u0111\u1ED9ng: ", formatRelativeTime(session.lastActiveAt)] })] })), _jsxs("div", { className: "text-xs text-gray-500", children: ["T\u1EA1o l\u00FAc: ", formatDate(session.createdAt)] })] })] })] }), _jsxs("button", { onClick: () => handleRevokeSession(session.id), disabled: revoking === session.id, className: "px-3 py-2 text-sm font-medium text-red-600 hover:bg-red-50 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2", children: [_jsx(Trash2, { className: "w-4 h-4" }), revoking === session.id ? 'Đang xóa...' : 'Thu hồi'] })] }) }, session.id)))) })] }));
92
+ }
93
+ //# sourceMappingURL=SessionsSection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionsSection.js","sourceRoot":"","sources":["../../../../src/client/pages/users/SessionsSection.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ declare enum SocialProvider {
2
+ DEFAULT = 0,
3
+ GOOGLE = 1,
4
+ FACEBOOK = 2,
5
+ GITHUB = 3
6
+ }
7
+ interface SocialAccount {
8
+ id: number;
9
+ userId: number;
10
+ provider: SocialProvider;
11
+ providerUserId: string;
12
+ email?: string;
13
+ displayName?: string;
14
+ avatar?: string;
15
+ accessToken?: string;
16
+ refreshToken?: string;
17
+ tokenExpiresAt?: string;
18
+ profileData?: any;
19
+ createdAt: string;
20
+ updatedAt: string;
21
+ }
22
+ interface SocialAccountsSectionProps {
23
+ userId: string;
24
+ accounts: SocialAccount[];
25
+ onRefresh: () => void;
26
+ }
27
+ export declare function SocialAccountsSection({ userId, accounts, onRefresh }: SocialAccountsSectionProps): import("react/jsx-runtime").JSX.Element;
28
+ export {};
29
+ //# sourceMappingURL=SocialAccountsSection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SocialAccountsSection.d.ts","sourceRoot":"","sources":["../../../../src/client/pages/users/SocialAccountsSection.tsx"],"names":[],"mappings":"AAQA,aAAK,cAAc;IACjB,OAAO,IAAI;IACX,MAAM,IAAI;IACV,QAAQ,IAAI;IACZ,MAAM,IAAI;CACX;AAED,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,cAAc,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,GAAG,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,UAAU,0BAA0B;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB;AA6BD,wBAAgB,qBAAqB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,0BAA0B,2CA+GhG"}
@@ -0,0 +1,72 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { Link2, Trash2, Mail, Calendar } from 'lucide-react';
5
+ import toast from 'react-hot-toast';
6
+ import { getApiClient } from '../../auth/api-client';
7
+ import { CORE_API_ROUTES } from '../../constants/api-routes';
8
+ var SocialProvider;
9
+ (function (SocialProvider) {
10
+ SocialProvider[SocialProvider["DEFAULT"] = 0] = "DEFAULT";
11
+ SocialProvider[SocialProvider["GOOGLE"] = 1] = "GOOGLE";
12
+ SocialProvider[SocialProvider["FACEBOOK"] = 2] = "FACEBOOK";
13
+ SocialProvider[SocialProvider["GITHUB"] = 3] = "GITHUB";
14
+ })(SocialProvider || (SocialProvider = {}));
15
+ const PROVIDER_CONFIG = {
16
+ [SocialProvider.GOOGLE]: {
17
+ name: 'Google',
18
+ icon: '',
19
+ bgColor: 'bg-blue-50',
20
+ textColor: 'text-blue-700',
21
+ },
22
+ [SocialProvider.FACEBOOK]: {
23
+ name: 'Facebook',
24
+ icon: '',
25
+ bgColor: 'bg-blue-50',
26
+ textColor: 'text-blue-600',
27
+ },
28
+ [SocialProvider.GITHUB]: {
29
+ name: 'GitHub',
30
+ icon: '',
31
+ bgColor: 'bg-gray-50',
32
+ textColor: 'text-gray-900',
33
+ },
34
+ [SocialProvider.DEFAULT]: {
35
+ name: 'Unknown',
36
+ icon: '?',
37
+ bgColor: 'bg-gray-50',
38
+ textColor: 'text-gray-600',
39
+ },
40
+ };
41
+ export function SocialAccountsSection({ userId, accounts, onRefresh }) {
42
+ const apiClient = getApiClient();
43
+ const [unlinking, setUnlinking] = useState(null);
44
+ const handleUnlinkAccount = async (accountId, providerName) => {
45
+ if (!confirm(`Bạn có chắc chắn muốn hủy liên kết tài khoản ${providerName}?`))
46
+ return;
47
+ setUnlinking(accountId);
48
+ try {
49
+ await apiClient.delete(CORE_API_ROUTES.ADMIN_USERS.UNLINK_SOCIAL_ACCOUNT(userId, accountId));
50
+ toast.success('Đã hủy liên kết tài khoản');
51
+ onRefresh();
52
+ }
53
+ catch (error) {
54
+ toast.error(error.response?.data?.message || 'Không thể hủy liên kết tài khoản');
55
+ }
56
+ finally {
57
+ setUnlinking(null);
58
+ }
59
+ };
60
+ const formatDate = (date) => {
61
+ return new Date(date).toLocaleDateString('vi-VN', {
62
+ year: 'numeric',
63
+ month: 'long',
64
+ day: 'numeric',
65
+ });
66
+ };
67
+ return (_jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-200", children: [_jsx("div", { className: "p-6 border-b border-gray-200", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center", children: _jsx(Link2, { className: "w-5 h-5 text-purple-600" }) }), _jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "T\u00E0i kho\u1EA3n li\u00EAn k\u1EBFt" }), _jsxs("p", { className: "text-sm text-gray-500", children: [accounts.length, " t\u00E0i kho\u1EA3n m\u1EA1ng x\u00E3 h\u1ED9i"] })] })] }) }), _jsx("div", { className: "divide-y divide-gray-200", children: accounts.length === 0 ? (_jsxs("div", { className: "p-12 text-center", children: [_jsx(Link2, { className: "w-12 h-12 text-gray-300 mx-auto mb-3" }), _jsx("p", { className: "text-gray-500", children: "Ch\u01B0a li\u00EAn k\u1EBFt t\u00E0i kho\u1EA3n n\u00E0o" }), _jsx("p", { className: "text-sm text-gray-400 mt-1", children: "User ch\u01B0a \u0111\u0103ng nh\u1EADp qua m\u1EA1ng x\u00E3 h\u1ED9i" })] })) : (accounts.map((account) => {
68
+ const config = PROVIDER_CONFIG[account.provider] || PROVIDER_CONFIG[SocialProvider.DEFAULT];
69
+ return (_jsx("div", { className: "p-6 hover:bg-gray-50 transition-colors", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-start gap-4 flex-1", children: [_jsx("div", { className: `w-12 h-12 ${config.bgColor} rounded-lg flex items-center justify-center text-2xl flex-shrink-0`, children: config.icon }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "flex items-center gap-2 mb-1", children: _jsx("h3", { className: `text-base font-semibold ${config.textColor}`, children: config.name }) }), _jsxs("div", { className: "space-y-1.5 mt-2", children: [account.displayName && (_jsx("div", { className: "text-sm text-gray-900", children: account.displayName })), account.email && (_jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: [_jsx(Mail, { className: "w-4 h-4" }), _jsx("span", { children: account.email })] })), _jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [_jsx(Calendar, { className: "w-4 h-4" }), _jsxs("span", { children: ["Li\u00EAn k\u1EBFt l\u00FAc: ", formatDate(account.createdAt)] })] })] })] })] }), _jsxs("button", { onClick: () => handleUnlinkAccount(account.id, config.name), disabled: unlinking === account.id, className: "px-3 py-2 text-sm font-medium text-red-600 hover:bg-red-50 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2", children: [_jsx(Trash2, { className: "w-4 h-4" }), unlinking === account.id ? 'Đang xóa...' : 'Hủy liên kết'] })] }) }, account.id));
70
+ })) })] }));
71
+ }
72
+ //# sourceMappingURL=SocialAccountsSection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SocialAccountsSection.js","sourceRoot":"","sources":["../../../../src/client/pages/users/SocialAccountsSection.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ interface User {
2
+ id: number;
3
+ email: string;
4
+ name: string;
5
+ role: string;
6
+ isActive: boolean;
7
+ createdAt: string;
8
+ avatar?: string;
9
+ emailVerified?: boolean;
10
+ emailVerifiedAt?: string;
11
+ twoFactorEnabled?: boolean;
12
+ deletedAt?: string;
13
+ lastLoginAt?: string;
14
+ failedLoginAttempts?: number;
15
+ lockedUntil?: string;
16
+ passwordChangedAt?: string;
17
+ }
18
+ interface UserDetailModalProps {
19
+ user: User | null;
20
+ onClose: () => void;
21
+ }
22
+ export declare const UserDetailModal: ({ user, onClose }: UserDetailModalProps) => import("react/jsx-runtime").JSX.Element | null;
23
+ export {};
24
+ //# sourceMappingURL=UserDetailModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserDetailModal.d.ts","sourceRoot":"","sources":["../../../../src/client/pages/users/UserDetailModal.tsx"],"names":[],"mappings":"AAGA,UAAU,IAAI;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,UAAU,oBAAoB;IAC5B,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;IACjB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,eAAO,MAAM,eAAe,GAAI,mBAAmB,oBAAoB,mDAqMtE,CAAA"}
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { X, Mail, Shield, Calendar, Clock, CheckCircle, XCircle } from 'lucide-react';
3
+ import Modal from '../../ui/Modal';
4
+ export const UserDetailModal = ({ user, onClose }) => {
5
+ if (!user)
6
+ return null;
7
+ const formatDate = (date) => {
8
+ if (!date)
9
+ return 'Chưa có';
10
+ return new Date(date).toLocaleString('vi-VN', {
11
+ year: 'numeric',
12
+ month: '2-digit',
13
+ day: '2-digit',
14
+ hour: '2-digit',
15
+ minute: '2-digit',
16
+ });
17
+ };
18
+ const getRoleName = (role) => {
19
+ const roles = {
20
+ viewer: 'Người xem',
21
+ editor: 'Biên tập viên',
22
+ admin: 'Quản trị viên',
23
+ super_admin: 'Quản trị viên cấp cao',
24
+ };
25
+ return roles[role] || role;
26
+ };
27
+ const getRoleBadgeColor = (role) => {
28
+ const colors = {
29
+ viewer: 'bg-base-200 text-base-content',
30
+ editor: 'bg-info/10 text-info',
31
+ admin: 'bg-primary/10 text-primary',
32
+ super_admin: 'bg-warning/10 text-warning',
33
+ };
34
+ return colors[role] || 'bg-base-200 text-base-content';
35
+ };
36
+ return (_jsxs(Modal, { isOpen: !!user, onClose: onClose, title: "", showCloseButton: false, size: "md", className: "max-w-2xl p-0 overflow-hidden", children: [_jsx("div", { className: "p-6 border-b border-base-200 bg-base-200/50", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "w-16 h-16 rounded-full bg-base-300 flex items-center justify-center overflow-hidden border-2 border-base-100 shadow-sm", children: user.avatar ? (_jsx("img", { src: user.avatar, alt: user.name, className: "w-full h-full object-cover" })) : (_jsx("span", { className: "text-2xl font-semibold text-base-content/50", children: user.name.charAt(0).toUpperCase() })) }), _jsxs("div", { children: [_jsx("h2", { className: "text-xl font-bold text-base-content", children: user.name }), _jsxs("div", { className: "flex items-center gap-2 mt-1", children: [_jsx(Mail, { className: "w-4 h-4 text-base-content/60" }), _jsx("p", { className: "text-sm text-base-content/60", children: user.email })] })] })] }), _jsx("button", { onClick: onClose, className: "p-2 hover:bg-base-200 rounded-lg transition-colors text-base-content/60 hover:text-base-content", children: _jsx(X, { className: "w-5 h-5" }) })] }) }), _jsx("div", { className: "p-6 overflow-y-auto max-h-[calc(90vh-140px)]", children: _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-base-content/50 uppercase tracking-wider mb-3", children: "Th\u00F4ng tin c\u01A1 b\u1EA3n" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(InfoItem, { label: "ID", value: user.id.toString() }), _jsx(InfoItem, { label: "Vai tr\u00F2", value: _jsx("span", { className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getRoleBadgeColor(user.role)}`, children: getRoleName(user.role) }) }), _jsx(InfoItem, { label: "Tr\u1EA1ng th\u00E1i t\u00E0i kho\u1EA3n", value: _jsxs("span", { className: `inline-flex items-center gap-1.5 px-2 py-1 rounded-md text-xs font-medium ${user.deletedAt ? 'bg-gray-50 text-gray-600' :
37
+ user.isActive ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'}`, children: [_jsx("div", { className: `w-1.5 h-1.5 rounded-full ${user.deletedAt ? 'bg-gray-400' :
38
+ user.isActive ? 'bg-green-500' : 'bg-red-500'}` }), user.deletedAt ? 'Đã xóa' : user.isActive ? 'Hoạt động' : 'Bị khóa'] }) }), _jsx(InfoItem, { label: "Email x\u00E1c th\u1EF1c", value: _jsx("span", { className: "inline-flex items-center gap-1", children: user.emailVerified ? (_jsxs(_Fragment, { children: [_jsx(CheckCircle, { className: "w-4 h-4 text-green-600" }), _jsx("span", { className: "text-green-700 text-sm font-medium", children: "\u0110\u00E3 x\u00E1c th\u1EF1c" })] })) : (_jsxs(_Fragment, { children: [_jsx(XCircle, { className: "w-4 h-4 text-gray-400" }), _jsx("span", { className: "text-gray-500 text-sm", children: "Ch\u01B0a x\u00E1c th\u1EF1c" })] })) }) })] })] }), _jsxs("div", { className: "pt-4 border-t border-base-200", children: [_jsx("h3", { className: "text-sm font-semibold text-base-content/50 uppercase tracking-wider mb-3", children: "B\u1EA3o m\u1EADt" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(InfoItem, { label: "X\u00E1c th\u1EF1c 2 b\u01B0\u1EDBc (2FA)", value: _jsx("span", { className: "inline-flex items-center gap-1.5", children: user.twoFactorEnabled ? (_jsxs(_Fragment, { children: [_jsx(Shield, { className: "w-4 h-4 text-purple-600" }), _jsx("span", { className: "text-purple-700 text-sm font-medium", children: "\u0110\u00E3 b\u1EADt" })] })) : (_jsxs(_Fragment, { children: [_jsx(Shield, { className: "w-4 h-4 text-gray-300" }), _jsx("span", { className: "text-gray-500 text-sm", children: "Ch\u01B0a b\u1EADt" })] })) }) }), _jsx(InfoItem, { label: "S\u1ED1 l\u1EA7n \u0111\u0103ng nh\u1EADp sai", value: user.failedLoginAttempts?.toString() || '0' }), _jsx(InfoItem, { label: "Kh\u00F3a \u0111\u1EBFn", value: user.lockedUntil ? formatDate(user.lockedUntil) : 'Không bị khóa' }), _jsx(InfoItem, { label: "M\u1EADt kh\u1EA9u thay \u0111\u1ED5i l\u00FAc", value: formatDate(user.passwordChangedAt) })] })] }), _jsxs("div", { className: "pt-4 border-t border-base-200", children: [_jsx("h3", { className: "text-sm font-semibold text-base-content/50 uppercase tracking-wider mb-3", children: "Ho\u1EA1t \u0111\u1ED9ng" }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(InfoItem, { label: "Ng\u00E0y t\u1EA1o", value: formatDate(user.createdAt), icon: _jsx(Calendar, { className: "w-4 h-4 text-gray-400" }) }), _jsx(InfoItem, { label: "\u0110\u0103ng nh\u1EADp l\u1EA7n cu\u1ED1i", value: formatDate(user.lastLoginAt), icon: _jsx(Clock, { className: "w-4 h-4 text-gray-400" }) }), _jsx(InfoItem, { label: "Email x\u00E1c th\u1EF1c l\u00FAc", value: formatDate(user.emailVerifiedAt) }), _jsx(InfoItem, { label: "Ng\u00E0y x\u00F3a", value: formatDate(user.deletedAt) })] })] })] }) })] }));
39
+ };
40
+ // Helper component for displaying info items
41
+ const InfoItem = ({ label, value, icon }) => (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-1.5 mb-1.5", children: [icon, _jsx("label", { className: "text-xs font-medium text-base-content/50 uppercase tracking-wide", children: label })] }), _jsx("div", { className: "text-sm text-base-content font-medium", children: value || _jsx("span", { className: "text-base-content/40 italic", children: "Ch\u01B0a c\u00F3 d\u1EEF li\u1EC7u" }) })] }));
42
+ //# sourceMappingURL=UserDetailModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserDetailModal.js","sourceRoot":"","sources":["../../../../src/client/pages/users/UserDetailModal.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ interface UserFiltersProps {
2
+ searchTerm: string;
3
+ onSearchChange: (value: string) => void;
4
+ roleFilter: string;
5
+ onRoleFilterChange: (value: string) => void;
6
+ statusFilter: string;
7
+ onStatusFilterChange: (value: string) => void;
8
+ showDeleted: boolean;
9
+ onShowDeletedChange: (value: boolean) => void;
10
+ onClearFilters: () => void;
11
+ }
12
+ export declare const UserFilters: ({ searchTerm, onSearchChange, roleFilter, onRoleFilterChange, statusFilter, onStatusFilterChange, showDeleted, onShowDeletedChange, onClearFilters, }: UserFiltersProps) => import("react/jsx-runtime").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=UserFilters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserFilters.d.ts","sourceRoot":"","sources":["../../../../src/client/pages/users/UserFilters.tsx"],"names":[],"mappings":"AAKA,UAAU,gBAAgB;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,MAAM,CAAA;IACpB,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7C,WAAW,EAAE,OAAO,CAAA;IACpB,mBAAmB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7C,cAAc,EAAE,MAAM,IAAI,CAAA;CAC3B;AAED,eAAO,MAAM,WAAW,GAAI,uJAUzB,gBAAgB,4CAoHlB,CAAA"}
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Search, Filter } from 'lucide-react';
4
+ import { useState, useRef, useEffect } from 'react';
5
+ export const UserFilters = ({ searchTerm, onSearchChange, roleFilter, onRoleFilterChange, statusFilter, onStatusFilterChange, showDeleted, onShowDeletedChange, onClearFilters, }) => {
6
+ const [showFilters, setShowFilters] = useState(false);
7
+ const filterRef = useRef(null);
8
+ const activeFiltersCount = [roleFilter, statusFilter].filter(Boolean).length + (showDeleted ? 1 : 0);
9
+ useEffect(() => {
10
+ const handleClickOutside = (event) => {
11
+ if (filterRef.current && !filterRef.current.contains(event.target)) {
12
+ setShowFilters(false);
13
+ }
14
+ };
15
+ document.addEventListener('mousedown', handleClickOutside);
16
+ return () => document.removeEventListener('mousedown', handleClickOutside);
17
+ }, []);
18
+ return (_jsxs("div", { className: "flex flex-col sm:flex-row gap-4 mb-6", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" }), _jsx("input", { type: "text", placeholder: "T\u00ECm ki\u1EBFm theo t\u00EAn, email...", value: searchTerm, onChange: (e) => onSearchChange(e.target.value), className: "w-full pl-10 pr-4 py-2.5 bg-white rounded-lg border border-gray-200 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary shadow-sm transition-all" })] }), _jsxs("div", { className: "relative", ref: filterRef, children: [_jsxs("button", { onClick: () => setShowFilters(!showFilters), className: `flex items-center gap-2 px-4 py-2.5 rounded-lg border transition-all ${activeFiltersCount > 0
19
+ ? 'bg-primary/5 border-primary/20 text-primary'
20
+ : 'bg-white border-gray-200 text-gray-700 hover:bg-gray-50'}`, children: [_jsx(Filter, { className: "w-4 h-4" }), _jsx("span", { className: "font-medium", children: "B\u1ED9 l\u1ECDc" }), activeFiltersCount > 0 && (_jsx("span", { className: "flex items-center justify-center w-5 h-5 bg-primary text-white text-xs rounded-full ml-1", children: activeFiltersCount }))] }), showFilters && (_jsxs("div", { className: "absolute right-0 mt-2 w-72 bg-white rounded-xl border border-gray-200 shadow-lg z-30 animate-in fade-in zoom-in-95 duration-200 p-4", children: [_jsxs("div", { className: "flex justify-between items-center mb-4", children: [_jsx("h3", { className: "font-semibold text-gray-900", children: "B\u1ED9 l\u1ECDc t\u00ECm ki\u1EBFm" }), activeFiltersCount > 0 && (_jsx("button", { onClick: onClearFilters, className: "text-xs text-red-600 hover:text-red-700 font-medium", children: "X\u00F3a b\u1ED9 l\u1ECDc" }))] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-gray-500 mb-1.5 uppercase", children: "Vai tr\u00F2" }), _jsxs("select", { value: roleFilter, onChange: (e) => onRoleFilterChange(e.target.value), className: "w-full px-3 py-2 rounded-lg border border-gray-200 text-sm focus:outline-none focus:border-primary", children: [_jsx("option", { value: "", children: "T\u1EA5t c\u1EA3 vai tr\u00F2" }), _jsx("option", { value: "viewer", children: "Viewer" }), _jsx("option", { value: "editor", children: "Editor" }), _jsx("option", { value: "admin", children: "Admin" }), _jsx("option", { value: "super_admin", children: "Super Admin" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-gray-500 mb-1.5 uppercase", children: "Tr\u1EA1ng th\u00E1i" }), _jsxs("select", { value: statusFilter, onChange: (e) => onStatusFilterChange(e.target.value), className: "w-full px-3 py-2 rounded-lg border border-gray-200 text-sm focus:outline-none focus:border-primary", children: [_jsx("option", { value: "", children: "T\u1EA5t c\u1EA3 tr\u1EA1ng th\u00E1i" }), _jsx("option", { value: "ACTIVE", children: "Ho\u1EA1t \u0111\u1ED9ng" }), _jsx("option", { value: "INACTIVE", children: "Kh\u00F4ng ho\u1EA1t \u0111\u1ED9ng/B\u1ECB kh\u00F3a" })] })] }), _jsx("div", { className: "pt-2 border-t border-gray-50", children: _jsxs("label", { className: "flex items-center gap-2 cursor-pointer group", children: [_jsx("div", { className: `w-4 h-4 rounded border flex items-center justify-center transition-colors ${showDeleted ? 'bg-primary border-primary' : 'border-gray-300 bg-white'}`, children: showDeleted && _jsx("div", { className: "w-2 h-2 bg-white rounded-sm" }) }), _jsx("input", { type: "checkbox", checked: showDeleted, onChange: (e) => onShowDeletedChange(e.target.checked), className: "hidden" }), _jsx("span", { className: "text-sm text-gray-700 group-hover:text-gray-900", children: "Hi\u1EC3n th\u1ECB \u0111\u00E3 x\u00F3a" })] }) })] })] }))] })] }));
21
+ };
22
+ //# sourceMappingURL=UserFilters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserFilters.js","sourceRoot":"","sources":["../../../../src/client/pages/users/UserFilters.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ interface UserStatsProps {
2
+ total: number;
3
+ activeCount: number;
4
+ verifiedCount: number;
5
+ newUsersCount: number;
6
+ }
7
+ export declare const UserStats: ({ total, activeCount, verifiedCount, newUsersCount }: Partial<UserStatsProps>) => import("react/jsx-runtime").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=UserStats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserStats.d.ts","sourceRoot":"","sources":["../../../../src/client/pages/users/UserStats.tsx"],"names":[],"mappings":"AAGA,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,eAAO,MAAM,SAAS,GAAI,sDAAsE,OAAO,CAAC,cAAc,CAAC,4CAiDtH,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Users, UserCheck, UserPlus, ShieldAlert } from 'lucide-react';
3
+ export const UserStats = ({ total = 0, activeCount = 0, verifiedCount = 0, newUsersCount = 0 }) => {
4
+ const stats = [
5
+ {
6
+ label: 'Tổng người dùng',
7
+ value: total,
8
+ icon: Users,
9
+ color: 'text-blue-600',
10
+ bgCallback: 'bg-blue-50',
11
+ },
12
+ {
13
+ label: 'Đang hoạt động',
14
+ value: activeCount,
15
+ icon: UserCheck,
16
+ color: 'text-green-600',
17
+ bgCallback: 'bg-green-50',
18
+ },
19
+ {
20
+ label: 'Đã xác thực',
21
+ value: verifiedCount,
22
+ icon: ShieldAlert,
23
+ color: 'text-purple-600',
24
+ bgCallback: 'bg-purple-50',
25
+ },
26
+ {
27
+ label: 'Người dùng mới',
28
+ value: newUsersCount,
29
+ icon: UserPlus,
30
+ color: 'text-orange-600',
31
+ bgCallback: 'bg-orange-50',
32
+ subtext: '7 ngày qua'
33
+ },
34
+ ];
35
+ return (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6", children: stats.map((stat, index) => (_jsxs("div", { className: "bg-base-100 p-4 rounded-xl border border-base-200 flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-base-content/60 mb-1", children: stat.label }), _jsx("h3", { className: "text-2xl font-bold text-base-content", children: stat.value.toLocaleString() }), stat.subtext && _jsx("p", { className: "text-xs text-base-content/40 mt-1", children: stat.subtext })] }), _jsx("div", { className: `p-3 rounded-lg ${stat.bgCallback}`, children: _jsx(stat.icon, { className: `w-6 h-6 ${stat.color}` }) })] }, index))) }));
36
+ };
37
+ //# sourceMappingURL=UserStats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserStats.js","sourceRoot":"","sources":["../../../../src/client/pages/users/UserStats.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAStE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,WAAW,GAAG,CAAC,EAAE,aAAa,GAAG,CAAC,EAAE,aAAa,GAAG,CAAC,EAA2B,EAAE,EAAE;IACzH,MAAM,KAAK,GAAG;QACZ;YACE,KAAK,EAAE,iBAAiB;YACxB,KAAK,EAAE,KAAK;YACZ,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,eAAe;YACtB,UAAU,EAAE,YAAY;SACzB;QACD;YACE,KAAK,EAAE,gBAAgB;YACvB,KAAK,EAAE,WAAW;YAClB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,gBAAgB;YACvB,UAAU,EAAE,aAAa;SAC1B;QACD;YACE,KAAK,EAAE,aAAa;YACpB,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,iBAAiB;YACxB,UAAU,EAAE,cAAc;SAC3B;QACD;YACE,KAAK,EAAE,gBAAgB;YACvB,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,iBAAiB;YACxB,UAAU,EAAE,cAAc;YAC1B,OAAO,EAAE,YAAY;SACtB;KACF,CAAA;IAED,OAAO,CACL,cAAK,SAAS,EAAC,2DAA2D,YACvE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC1B,eAAiB,SAAS,EAAC,qFAAqF,aAC9G,0BACE,YAAG,SAAS,EAAC,+CAA+C,YAAE,IAAI,CAAC,KAAK,GAAK,EAC7E,aAAI,SAAS,EAAC,sCAAsC,YAAE,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,GAAM,EACtF,IAAI,CAAC,OAAO,IAAI,YAAG,SAAS,EAAC,mCAAmC,YAAE,IAAI,CAAC,OAAO,GAAK,IAChF,EACN,cAAK,SAAS,EAAE,kBAAkB,IAAI,CAAC,UAAU,EAAE,YACjD,KAAC,IAAI,CAAC,IAAI,IAAC,SAAS,EAAE,WAAW,IAAI,CAAC,KAAK,EAAE,GAAI,GAC7C,KARE,KAAK,CAST,CACP,CAAC,GACE,CACP,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { User } from 'lucide-react';
2
+ interface User {
3
+ id: number;
4
+ email: string;
5
+ name: string;
6
+ role: string;
7
+ isActive: boolean;
8
+ createdAt: string;
9
+ avatar?: string;
10
+ emailVerified?: boolean;
11
+ twoFactorEnabled?: boolean;
12
+ deletedAt?: string;
13
+ }
14
+ interface UserTableProps {
15
+ users: User[];
16
+ onDelete: (id: number) => void;
17
+ onRestore: (id: number) => void;
18
+ onUpdateStatus: (id: number, status: string) => void;
19
+ onVerifyEmail: (id: number) => void;
20
+ onUserClick: (user: User) => void;
21
+ }
22
+ export declare const UserTable: ({ users, onDelete, onRestore, onUpdateStatus, onVerifyEmail, onUserClick }: UserTableProps) => import("react/jsx-runtime").JSX.Element;
23
+ export {};
24
+ //# sourceMappingURL=UserTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserTable.d.ts","sourceRoot":"","sources":["../../../../src/client/pages/users/UserTable.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAyD,IAAI,EAA4B,MAAM,cAAc,CAAA;AAIpH,UAAU,IAAI;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,UAAU,cAAc;IACtB,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACpD,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;CAClC;AAED,eAAO,MAAM,SAAS,GAAI,4EAA4E,cAAc,4CAiMnH,CAAA"}