@hed-hog/core 0.0.350 → 0.0.353

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 (284) hide show
  1. package/dist/access-log/access-log.controller.d.ts +16 -0
  2. package/dist/access-log/access-log.controller.d.ts.map +1 -0
  3. package/dist/access-log/access-log.controller.js +44 -0
  4. package/dist/access-log/access-log.controller.js.map +1 -0
  5. package/dist/access-log/access-log.interceptor.d.ts +9 -0
  6. package/dist/access-log/access-log.interceptor.d.ts.map +1 -0
  7. package/dist/access-log/access-log.interceptor.js +46 -0
  8. package/dist/access-log/access-log.interceptor.js.map +1 -0
  9. package/dist/access-log/access-log.module.d.ts +3 -0
  10. package/dist/access-log/access-log.module.d.ts.map +1 -0
  11. package/dist/access-log/access-log.module.js +29 -0
  12. package/dist/access-log/access-log.module.js.map +1 -0
  13. package/dist/access-log/access-log.service.d.ts +47 -0
  14. package/dist/access-log/access-log.service.d.ts.map +1 -0
  15. package/dist/access-log/access-log.service.js +113 -0
  16. package/dist/access-log/access-log.service.js.map +1 -0
  17. package/dist/access-log/dto/query-access-log.dto.d.ts +8 -0
  18. package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -0
  19. package/dist/access-log/dto/query-access-log.dto.js +42 -0
  20. package/dist/access-log/dto/query-access-log.dto.js.map +1 -0
  21. package/dist/auth/auth.controller.d.ts +9 -9
  22. package/dist/auth/auth.service.d.ts +19 -19
  23. package/dist/auth/auth.service.d.ts.map +1 -1
  24. package/dist/auth/auth.service.js +0 -4
  25. package/dist/auth/auth.service.js.map +1 -1
  26. package/dist/challenge/challenge.service.d.ts +2 -2
  27. package/dist/challenge/challenge.service.d.ts.map +1 -1
  28. package/dist/challenge/challenge.service.js +0 -1
  29. package/dist/challenge/challenge.service.js.map +1 -1
  30. package/dist/core/system.module.d.ts.map +1 -1
  31. package/dist/core/system.module.js +1 -0
  32. package/dist/core/system.module.js.map +1 -1
  33. package/dist/core.module.d.ts.map +1 -1
  34. package/dist/core.module.js +12 -0
  35. package/dist/core.module.js.map +1 -1
  36. package/dist/dashboard/dashboard-user/dashboard-user.controller.d.ts +3 -3
  37. package/dist/dashboard/dashboard-user/dashboard-user.service.d.ts +3 -3
  38. package/dist/dashboard/dashboard.module.d.ts.map +1 -1
  39. package/dist/dashboard/dashboard.module.js +4 -1
  40. package/dist/dashboard/dashboard.module.js.map +1 -1
  41. package/dist/file/file.controller.js +0 -1
  42. package/dist/file/file.controller.js.map +1 -1
  43. package/dist/file/file.service.d.ts +65 -0
  44. package/dist/file/file.service.d.ts.map +1 -1
  45. package/dist/file/file.service.js +151 -3
  46. package/dist/file/file.service.js.map +1 -1
  47. package/dist/file/provider/abstract.provider.d.ts +8 -0
  48. package/dist/file/provider/abstract.provider.d.ts.map +1 -1
  49. package/dist/file/provider/abstract.provider.js.map +1 -1
  50. package/dist/file/provider/azure.provider.d.ts +4 -0
  51. package/dist/file/provider/azure.provider.d.ts.map +1 -1
  52. package/dist/file/provider/azure.provider.js +14 -0
  53. package/dist/file/provider/azure.provider.js.map +1 -1
  54. package/dist/file/provider/gcs.provider.d.ts +4 -0
  55. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  56. package/dist/file/provider/gcs.provider.js +14 -0
  57. package/dist/file/provider/gcs.provider.js.map +1 -1
  58. package/dist/file/provider/local.provider.d.ts +4 -0
  59. package/dist/file/provider/local.provider.d.ts.map +1 -1
  60. package/dist/file/provider/local.provider.js +19 -0
  61. package/dist/file/provider/local.provider.js.map +1 -1
  62. package/dist/file/provider/provider.factory.d.ts.map +1 -1
  63. package/dist/file/provider/provider.factory.js +3 -0
  64. package/dist/file/provider/provider.factory.js.map +1 -1
  65. package/dist/file/provider/s3.provider.d.ts +4 -0
  66. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  67. package/dist/file/provider/s3.provider.js +17 -0
  68. package/dist/file/provider/s3.provider.js.map +1 -1
  69. package/dist/mcp/mcp-audit.service.d.ts +3 -1
  70. package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
  71. package/dist/mcp/mcp-audit.service.js +14 -2
  72. package/dist/mcp/mcp-audit.service.js.map +1 -1
  73. package/dist/mcp/mcp.controller.d.ts +3 -1
  74. package/dist/mcp/mcp.controller.d.ts.map +1 -1
  75. package/dist/mcp/mcp.controller.js +10 -2
  76. package/dist/mcp/mcp.controller.js.map +1 -1
  77. package/dist/mcp/mcp.module.d.ts.map +1 -1
  78. package/dist/mcp/mcp.module.js +72 -0
  79. package/dist/mcp/mcp.module.js.map +1 -1
  80. package/dist/mcp/tools/core-ai.mcp-tools.d.ts +45 -0
  81. package/dist/mcp/tools/core-ai.mcp-tools.d.ts.map +1 -0
  82. package/dist/mcp/tools/core-ai.mcp-tools.js +214 -0
  83. package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -0
  84. package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts +21 -0
  85. package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts.map +1 -0
  86. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +89 -0
  87. package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -0
  88. package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts +29 -0
  89. package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts.map +1 -0
  90. package/dist/mcp/tools/core-dashboard.mcp-tools.js +149 -0
  91. package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -0
  92. package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts +30 -0
  93. package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -0
  94. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +185 -0
  95. package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -0
  96. package/dist/mcp/tools/core-files.mcp-tools.d.ts +26 -0
  97. package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -0
  98. package/dist/mcp/tools/core-files.mcp-tools.js +140 -0
  99. package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -0
  100. package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts +26 -0
  101. package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts.map +1 -0
  102. package/dist/mcp/tools/core-integration-events.mcp-tools.js +168 -0
  103. package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -0
  104. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +32 -0
  105. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +1 -0
  106. package/dist/mcp/tools/core-mail-profile.mcp-tools.js +178 -0
  107. package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +1 -0
  108. package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts +14 -0
  109. package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts.map +1 -0
  110. package/dist/mcp/tools/core-mail-sent.mcp-tools.js +106 -0
  111. package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -0
  112. package/dist/mcp/tools/core-mail.mcp-tools.d.ts +36 -0
  113. package/dist/mcp/tools/core-mail.mcp-tools.d.ts.map +1 -0
  114. package/dist/mcp/tools/core-mail.mcp-tools.js +184 -0
  115. package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -0
  116. package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts +21 -0
  117. package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts.map +1 -0
  118. package/dist/mcp/tools/core-mcp-chat.mcp-tools.js +118 -0
  119. package/dist/mcp/tools/core-mcp-chat.mcp-tools.js.map +1 -0
  120. package/dist/mcp/tools/core-menus.mcp-tools.d.ts +40 -0
  121. package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -0
  122. package/dist/mcp/tools/core-menus.mcp-tools.js +268 -0
  123. package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -0
  124. package/dist/mcp/tools/core-notifications.mcp-tools.d.ts +31 -0
  125. package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -0
  126. package/dist/mcp/tools/core-notifications.mcp-tools.js +178 -0
  127. package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -0
  128. package/dist/mcp/tools/core-profile.mcp-tools.d.ts +20 -0
  129. package/dist/mcp/tools/core-profile.mcp-tools.d.ts.map +1 -0
  130. package/dist/mcp/tools/core-profile.mcp-tools.js +111 -0
  131. package/dist/mcp/tools/core-profile.mcp-tools.js.map +1 -0
  132. package/dist/mcp/tools/core-roles.mcp-tools.d.ts +70 -0
  133. package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -0
  134. package/dist/mcp/tools/core-roles.mcp-tools.js +355 -0
  135. package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -0
  136. package/dist/mcp/tools/core-routes.mcp-tools.d.ts +52 -0
  137. package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -0
  138. package/dist/mcp/tools/core-routes.mcp-tools.js +254 -0
  139. package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -0
  140. package/dist/mcp/tools/core-screens.mcp-tools.d.ts +47 -0
  141. package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -0
  142. package/dist/mcp/tools/core-screens.mcp-tools.js +250 -0
  143. package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -0
  144. package/dist/mcp/tools/core-sessions.mcp-tools.d.ts +20 -0
  145. package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -0
  146. package/dist/mcp/tools/core-sessions.mcp-tools.js +128 -0
  147. package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -0
  148. package/dist/mcp/tools/core-settings.mcp-tools.d.ts +25 -0
  149. package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
  150. package/dist/mcp/tools/core-settings.mcp-tools.js +138 -0
  151. package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
  152. package/dist/mcp/tools/core-system.mcp-tools.d.ts +8 -0
  153. package/dist/mcp/tools/core-system.mcp-tools.d.ts.map +1 -0
  154. package/dist/mcp/tools/core-system.mcp-tools.js +40 -0
  155. package/dist/mcp/tools/core-system.mcp-tools.js.map +1 -0
  156. package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts +46 -0
  157. package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -0
  158. package/dist/mcp/tools/core-webhooks.mcp-tools.js +266 -0
  159. package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -0
  160. package/dist/mcp-chat/mcp-chat.controller.d.ts +1 -1
  161. package/dist/mcp-chat/mcp-chat.service.d.ts +25 -2
  162. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  163. package/dist/mcp-chat/mcp-chat.service.js +666 -36
  164. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  165. package/dist/notification/notification.controller.d.ts +8 -4
  166. package/dist/notification/notification.controller.d.ts.map +1 -1
  167. package/dist/notification/notification.controller.js +12 -0
  168. package/dist/notification/notification.controller.js.map +1 -1
  169. package/dist/notification/notification.service.d.ts +19 -5
  170. package/dist/notification/notification.service.d.ts.map +1 -1
  171. package/dist/notification/notification.service.js +155 -9
  172. package/dist/notification/notification.service.js.map +1 -1
  173. package/dist/pdf/pdf.service.js +1 -1
  174. package/dist/pdf/pdf.service.js.map +1 -1
  175. package/dist/profile/profile.controller.d.ts +3 -3
  176. package/dist/profile/profile.controller.js +0 -1
  177. package/dist/profile/profile.controller.js.map +1 -1
  178. package/dist/profile/profile.module.d.ts.map +1 -1
  179. package/dist/profile/profile.module.js +1 -0
  180. package/dist/profile/profile.module.js.map +1 -1
  181. package/dist/profile/profile.service.d.ts +3 -3
  182. package/dist/session/session.controller.d.ts +2 -2
  183. package/dist/session/session.service.d.ts +6 -6
  184. package/dist/setting/setting.controller.d.ts +1 -1
  185. package/dist/setting/setting.service.d.ts +1 -1
  186. package/dist/setting/setting.service.d.ts.map +1 -1
  187. package/dist/setting/setting.service.js +1 -4
  188. package/dist/setting/setting.service.js.map +1 -1
  189. package/dist/task/task.service.d.ts +1 -0
  190. package/dist/task/task.service.d.ts.map +1 -1
  191. package/dist/task/task.service.js +25 -0
  192. package/dist/task/task.service.js.map +1 -1
  193. package/dist/token/token.service.d.ts.map +1 -1
  194. package/dist/token/token.service.js +0 -1
  195. package/dist/token/token.service.js.map +1 -1
  196. package/dist/user/user.controller.d.ts +9 -9
  197. package/dist/user/user.service.d.ts +21 -21
  198. package/dist/webhook-integration/dto/webhook-action.dto.d.ts +22 -0
  199. package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -0
  200. package/dist/webhook-integration/dto/webhook-action.dto.js +120 -0
  201. package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -0
  202. package/dist/webhook-integration/webhook-integration.controller.d.ts +7 -5
  203. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  204. package/dist/webhook-integration/webhook-integration.controller.js +18 -5
  205. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  206. package/dist/webhook-integration/webhook-integration.service.d.ts +14 -5
  207. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  208. package/dist/webhook-integration/webhook-integration.service.js +174 -41
  209. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  210. package/hedhog/data/menu.yaml +15 -0
  211. package/hedhog/data/route.yaml +1304 -0
  212. package/hedhog/data/setting_group.yaml +67 -0
  213. package/hedhog/data/setting_subgroup.yaml +16 -0
  214. package/hedhog/frontend/app/access-log/page.tsx.ejs +772 -0
  215. package/hedhog/frontend/app/ai_agent/page.tsx.ejs +10 -3
  216. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +33 -13
  217. package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +12 -5
  218. package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +12 -5
  219. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +1817 -356
  220. package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +43 -5
  221. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +35 -7
  222. package/hedhog/frontend/app/menu/page.tsx.ejs +9 -3
  223. package/hedhog/frontend/app/roles/page.tsx.ejs +18 -6
  224. package/hedhog/frontend/app/routes/page.tsx.ejs +9 -3
  225. package/hedhog/frontend/app/setting-list/page.tsx.ejs +344 -171
  226. package/hedhog/frontend/app/users/user-access-sheet.tsx.ejs +310 -304
  227. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +25 -7
  228. package/hedhog/frontend/messages/en.json +104 -3
  229. package/hedhog/frontend/messages/pt.json +104 -3
  230. package/hedhog/table/access_log.yaml +31 -0
  231. package/package.json +4 -3
  232. package/src/access-log/access-log.controller.ts +29 -0
  233. package/src/access-log/access-log.interceptor.ts +38 -0
  234. package/src/access-log/access-log.module.ts +16 -0
  235. package/src/access-log/access-log.service.ts +129 -0
  236. package/src/access-log/dto/query-access-log.dto.ts +23 -0
  237. package/src/auth/auth.service.ts +0 -7
  238. package/src/challenge/challenge.service.ts +0 -2
  239. package/src/core/system.module.ts +1 -0
  240. package/src/core.module.ts +12 -0
  241. package/src/dashboard/dashboard.module.ts +4 -1
  242. package/src/file/file.controller.ts +14 -14
  243. package/src/file/file.service.ts +198 -4
  244. package/src/file/provider/abstract.provider.ts +10 -0
  245. package/src/file/provider/azure.provider.ts +24 -0
  246. package/src/file/provider/gcs.provider.ts +20 -0
  247. package/src/file/provider/local.provider.ts +30 -2
  248. package/src/file/provider/provider.factory.ts +3 -0
  249. package/src/file/provider/s3.provider.ts +22 -0
  250. package/src/mcp/mcp-audit.service.ts +47 -36
  251. package/src/mcp/mcp.controller.ts +40 -30
  252. package/src/mcp/mcp.module.ts +72 -0
  253. package/src/mcp/tools/core-ai.mcp-tools.ts +156 -0
  254. package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +63 -0
  255. package/src/mcp/tools/core-dashboard.mcp-tools.ts +103 -0
  256. package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +134 -0
  257. package/src/mcp/tools/core-files.mcp-tools.ts +105 -0
  258. package/src/mcp/tools/core-integration-events.mcp-tools.ts +118 -0
  259. package/src/mcp/tools/core-mail-profile.mcp-tools.ts +128 -0
  260. package/src/mcp/tools/core-mail-sent.mcp-tools.ts +76 -0
  261. package/src/mcp/tools/core-mail.mcp-tools.ts +130 -0
  262. package/src/mcp/tools/core-mcp-chat.mcp-tools.ts +81 -0
  263. package/src/mcp/tools/core-menus.mcp-tools.ts +197 -0
  264. package/src/mcp/tools/core-notifications.mcp-tools.ts +128 -0
  265. package/src/mcp/tools/core-profile.mcp-tools.ts +74 -0
  266. package/src/mcp/tools/core-roles.mcp-tools.ts +288 -0
  267. package/src/mcp/tools/core-routes.mcp-tools.ts +196 -0
  268. package/src/mcp/tools/core-screens.mcp-tools.ts +186 -0
  269. package/src/mcp/tools/core-sessions.mcp-tools.ts +97 -0
  270. package/src/mcp/tools/core-settings.mcp-tools.ts +171 -53
  271. package/src/mcp/tools/core-system.mcp-tools.ts +19 -0
  272. package/src/mcp/tools/core-webhooks.mcp-tools.ts +199 -0
  273. package/src/mcp-chat/mcp-chat.service.ts +887 -38
  274. package/src/notification/notification.controller.ts +20 -8
  275. package/src/notification/notification.service.ts +209 -9
  276. package/src/pdf/pdf.service.ts +1 -1
  277. package/src/profile/profile.controller.ts +1 -1
  278. package/src/profile/profile.module.ts +1 -0
  279. package/src/setting/setting.service.ts +8 -11
  280. package/src/task/task.service.ts +31 -0
  281. package/src/token/token.service.ts +0 -2
  282. package/src/webhook-integration/dto/webhook-action.dto.ts +96 -0
  283. package/src/webhook-integration/webhook-integration.controller.ts +124 -103
  284. package/src/webhook-integration/webhook-integration.service.ts +267 -64
@@ -1,304 +1,310 @@
1
- 'use client';
2
-
3
- import { Badge } from '@/components/ui/badge';
4
- import {
5
- Sheet,
6
- SheetContent,
7
- SheetDescription,
8
- SheetHeader,
9
- SheetTitle,
10
- } from '@/components/ui/sheet';
11
- import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
12
- import { useApp, useQuery } from '@hed-hog/next-app-provider';
13
- import { Globe, Loader2, Lock, Menu } from 'lucide-react';
14
- import { useTranslations } from 'next-intl';
15
-
16
- type MenuRole = { name: string; slug: string };
17
-
18
- type UserMenu = {
19
- id: number;
20
- menuId: number | null;
21
- name: string;
22
- url?: string;
23
- icon?: string;
24
- order?: number;
25
- roles?: MenuRole[];
26
- };
27
-
28
- type MenuNode = UserMenu & { children: MenuNode[] };
29
-
30
- type UserRoute = {
31
- id: number;
32
- url: string;
33
- method: string;
34
- roles?: MenuRole[];
35
- };
36
-
37
- type UserAccessSheetProps = {
38
- open: boolean;
39
- onOpenChange: (open: boolean) => void;
40
- userId: number;
41
- userName?: string;
42
- };
43
-
44
- const METHOD_COLORS: Record<string, string> = {
45
- GET: 'bg-green-50 text-green-700 border-green-200',
46
- POST: 'bg-blue-50 text-blue-700 border-blue-200',
47
- PATCH: 'bg-amber-50 text-amber-700 border-amber-200',
48
- PUT: 'bg-orange-50 text-orange-700 border-orange-200',
49
- DELETE: 'bg-rose-50 text-rose-700 border-rose-200',
50
- };
51
-
52
- function buildTree(flat: UserMenu[]): MenuNode[] {
53
- const map = new Map<number, MenuNode>();
54
- flat.forEach((m) => map.set(m.id, { ...m, children: [] }));
55
-
56
- const roots: MenuNode[] = [];
57
- map.forEach((node) => {
58
- if (node.menuId != null && map.has(node.menuId)) {
59
- map.get(node.menuId)!.children.push(node);
60
- } else {
61
- roots.push(node);
62
- }
63
- });
64
-
65
- const sortNodes = (nodes: MenuNode[]) => {
66
- nodes.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
67
- nodes.forEach((n) => sortNodes(n.children));
68
- };
69
- sortNodes(roots);
70
-
71
- return roots;
72
- }
73
-
74
- function MenuIcon({ name, className }: { name?: string; className?: string }) {
75
- if (name) {
76
- try {
77
- // eslint-disable-next-line @typescript-eslint/no-require-imports
78
- const { [name]: DynamicIcon } = require('lucide-react');
79
- if (DynamicIcon) return <DynamicIcon className={className} />;
80
- } catch {
81
- // fallback below
82
- }
83
- }
84
- return <Menu className={className} />;
85
- }
86
-
87
- function MenuTreeNode({ node, depth }: { node: MenuNode; depth: number }) {
88
- const isLeaf = node.children.length === 0;
89
- const indent = depth * 16;
90
-
91
- return (
92
- <>
93
- <div
94
- className="flex items-center gap-2 rounded py-1 px-1 hover:bg-muted/50 transition-colors group"
95
- style={{ paddingLeft: `${indent + 4}px` }}
96
- >
97
- {/* tree line connector */}
98
- {depth > 0 && (
99
- <span className="shrink-0 w-3 h-px bg-border" aria-hidden />
100
- )}
101
-
102
- <span
103
- className={`shrink-0 flex items-center justify-center rounded p-1 ${
104
- isLeaf ? 'bg-muted/60' : 'bg-primary/10'
105
- }`}
106
- >
107
- <MenuIcon
108
- name={node.icon}
109
- className={`h-3.5 w-3.5 ${isLeaf ? 'text-muted-foreground' : 'text-primary'}`}
110
- />
111
- </span>
112
-
113
- <div className="flex-1 min-w-0">
114
- <span className="text-sm leading-tight truncate block">
115
- {node.name}
116
- </span>
117
- {node.url && (
118
- <span className="text-xs text-muted-foreground truncate block font-mono">
119
- {node.url}
120
- </span>
121
- )}
122
- {node.roles && node.roles.length > 0 && (
123
- <div className="flex flex-wrap gap-1 mt-0.5">
124
- {node.roles.map((role) => (
125
- <span
126
- key={role.slug}
127
- className="inline-block rounded-full bg-muted px-1.5 py-0 text-[10px] font-medium text-muted-foreground leading-4"
128
- title={role.slug}
129
- >
130
- {role.name || role.slug}
131
- </span>
132
- ))}
133
- </div>
134
- )}
135
- </div>
136
- </div>
137
-
138
- {node.children.map((child) => (
139
- <MenuTreeNode key={child.id} node={child} depth={depth + 1} />
140
- ))}
141
- </>
142
- );
143
- }
144
-
145
- export function UserAccessSheet({
146
- open,
147
- onOpenChange,
148
- userId,
149
- userName,
150
- }: UserAccessSheetProps) {
151
- const t = useTranslations('core.UserPage');
152
- const { request, currentLocaleCode } = useApp();
153
-
154
- const { data: menus = [], isLoading: loadingMenus } = useQuery<UserMenu[]>({
155
- queryKey: ['user-menus', userId, currentLocaleCode],
156
- queryFn: async () => {
157
- const res = await request<UserMenu[]>({
158
- url: `/user/${userId}/menu`,
159
- method: 'GET',
160
- });
161
- return res.data || [];
162
- },
163
- enabled: open && !!userId,
164
- });
165
-
166
- const { data: routes = [], isLoading: loadingRoutes } = useQuery<UserRoute[]>(
167
- {
168
- queryKey: ['user-routes', userId],
169
- queryFn: async () => {
170
- const res = await request<UserRoute[]>({
171
- url: `/user/${userId}/route`,
172
- method: 'GET',
173
- });
174
- return res.data || [];
175
- },
176
- enabled: open && !!userId,
177
- }
178
- );
179
-
180
- const menuTree = buildTree(menus);
181
-
182
- return (
183
- <Sheet open={open} onOpenChange={onOpenChange}>
184
- <SheetContent className="w-full sm:max-w-xl overflow-y-auto gap-0">
185
- <SheetHeader className="px-6 pt-6 pb-4">
186
- <SheetTitle className="flex items-center gap-2">
187
- <Lock className="h-5 w-5 text-primary" />
188
- {t('accessViewTitle')}
189
- </SheetTitle>
190
- <SheetDescription>
191
- {userName
192
- ? t('accessViewDescriptionUser', { name: userName })
193
- : t('accessViewDescription')}
194
- </SheetDescription>
195
- </SheetHeader>
196
-
197
- <div className="w-full border-t" />
198
-
199
- <Tabs defaultValue="menu" className="px-6 pt-4">
200
- <TabsList className="w-full mb-4">
201
- <TabsTrigger
202
- value="menu"
203
- className="flex-1 flex items-center gap-2"
204
- >
205
- <Menu className="h-4 w-4" />
206
- {t('accessTabMenu')}
207
- {!loadingMenus && (
208
- <Badge variant="secondary" className="ml-1 text-xs">
209
- {menus.length}
210
- </Badge>
211
- )}
212
- </TabsTrigger>
213
- <TabsTrigger
214
- value="routes"
215
- className="flex-1 flex items-center gap-2"
216
- >
217
- <Globe className="h-4 w-4" />
218
- {t('accessTabRoutes')}
219
- {!loadingRoutes && (
220
- <Badge variant="secondary" className="ml-1 text-xs">
221
- {routes.length}
222
- </Badge>
223
- )}
224
- </TabsTrigger>
225
- </TabsList>
226
-
227
- <TabsContent value="menu" className="mt-0">
228
- {loadingMenus ? (
229
- <div className="flex items-center justify-center py-12">
230
- <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
231
- <span className="ml-2 text-sm text-muted-foreground">
232
- {t('loading')}
233
- </span>
234
- </div>
235
- ) : menuTree.length === 0 ? (
236
- <div className="flex flex-col items-center justify-center py-12 text-center">
237
- <Menu className="h-10 w-10 text-muted-foreground/40 mb-3" />
238
- <p className="text-sm text-muted-foreground">
239
- {t('accessNoMenus')}
240
- </p>
241
- </div>
242
- ) : (
243
- <div className="rounded-lg border border-border/60 bg-card px-2 py-2">
244
- {menuTree.map((node) => (
245
- <MenuTreeNode key={node.id} node={node} depth={0} />
246
- ))}
247
- </div>
248
- )}
249
- </TabsContent>
250
-
251
- <TabsContent value="routes" className="space-y-2 mt-0">
252
- {loadingRoutes ? (
253
- <div className="flex items-center justify-center py-12">
254
- <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
255
- <span className="ml-2 text-sm text-muted-foreground">
256
- {t('loading')}
257
- </span>
258
- </div>
259
- ) : routes.length === 0 ? (
260
- <div className="flex flex-col items-center justify-center py-12 text-center">
261
- <Globe className="h-10 w-10 text-muted-foreground/40 mb-3" />
262
- <p className="text-sm text-muted-foreground">
263
- {t('accessNoRoutes')}
264
- </p>
265
- </div>
266
- ) : (
267
- routes.map((route) => (
268
- <div
269
- key={route.id}
270
- className="flex items-start gap-3 rounded-lg border border-border/60 bg-card px-4 py-2.5 hover:border-primary/40 transition-colors"
271
- >
272
- <span
273
- className={`shrink-0 rounded border px-2 py-0.5 text-xs font-mono font-semibold ${
274
- METHOD_COLORS[route.method] ??
275
- 'bg-muted text-muted-foreground border-border'
276
- }`}
277
- >
278
- {route.method}
279
- </span>
280
- <div className="flex-1 min-w-0">
281
- <p className="text-sm font-mono truncate">{route.url}</p>
282
- {route.roles && route.roles.length > 0 && (
283
- <div className="flex flex-wrap gap-1 mt-0.5">
284
- {route.roles.map((role) => (
285
- <span
286
- key={role.slug}
287
- className="inline-block rounded-full bg-muted px-1.5 py-0 text-[10px] font-medium text-muted-foreground leading-4"
288
- title={role.slug}
289
- >
290
- {role.name || role.slug}
291
- </span>
292
- ))}
293
- </div>
294
- )}
295
- </div>
296
- </div>
297
- ))
298
- )}
299
- </TabsContent>
300
- </Tabs>
301
- </SheetContent>
302
- </Sheet>
303
- );
304
- }
1
+ 'use client';
2
+
3
+ import { Badge } from '@/components/ui/badge';
4
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
5
+ import {
6
+ Sheet,
7
+ SheetDescription,
8
+ SheetHeader,
9
+ SheetTitle,
10
+ } from '@/components/ui/sheet';
11
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
12
+ import { useApp, useQuery } from '@hed-hog/next-app-provider';
13
+ import { Globe, Loader2, Lock, Menu } from 'lucide-react';
14
+ import { useTranslations } from 'next-intl';
15
+
16
+ type MenuRole = { name: string; slug: string };
17
+
18
+ type UserMenu = {
19
+ id: number;
20
+ menuId: number | null;
21
+ name: string;
22
+ url?: string;
23
+ icon?: string;
24
+ order?: number;
25
+ roles?: MenuRole[];
26
+ };
27
+
28
+ type MenuNode = UserMenu & { children: MenuNode[] };
29
+
30
+ type UserRoute = {
31
+ id: number;
32
+ url: string;
33
+ method: string;
34
+ roles?: MenuRole[];
35
+ };
36
+
37
+ type UserAccessSheetProps = {
38
+ open: boolean;
39
+ onOpenChange: (open: boolean) => void;
40
+ userId: number;
41
+ userName?: string;
42
+ };
43
+
44
+ const METHOD_COLORS: Record<string, string> = {
45
+ GET: 'bg-green-50 text-green-700 border-green-200',
46
+ POST: 'bg-blue-50 text-blue-700 border-blue-200',
47
+ PATCH: 'bg-amber-50 text-amber-700 border-amber-200',
48
+ PUT: 'bg-orange-50 text-orange-700 border-orange-200',
49
+ DELETE: 'bg-rose-50 text-rose-700 border-rose-200',
50
+ };
51
+
52
+ function buildTree(flat: UserMenu[]): MenuNode[] {
53
+ const map = new Map<number, MenuNode>();
54
+ flat.forEach((m) => map.set(m.id, { ...m, children: [] }));
55
+
56
+ const roots: MenuNode[] = [];
57
+ map.forEach((node) => {
58
+ if (node.menuId != null && map.has(node.menuId)) {
59
+ map.get(node.menuId)!.children.push(node);
60
+ } else {
61
+ roots.push(node);
62
+ }
63
+ });
64
+
65
+ const sortNodes = (nodes: MenuNode[]) => {
66
+ nodes.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
67
+ nodes.forEach((n) => sortNodes(n.children));
68
+ };
69
+ sortNodes(roots);
70
+
71
+ return roots;
72
+ }
73
+
74
+ function MenuIcon({ name, className }: { name?: string; className?: string }) {
75
+ if (name) {
76
+ try {
77
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
78
+ const { [name]: DynamicIcon } = require('lucide-react');
79
+ if (DynamicIcon) return <DynamicIcon className={className} />;
80
+ } catch {
81
+ // fallback below
82
+ }
83
+ }
84
+ return <Menu className={className} />;
85
+ }
86
+
87
+ function MenuTreeNode({ node, depth }: { node: MenuNode; depth: number }) {
88
+ const isLeaf = node.children.length === 0;
89
+ const indent = depth * 16;
90
+
91
+ return (
92
+ <>
93
+ <div
94
+ className="flex items-center gap-2 rounded py-1 px-1 hover:bg-muted/50 transition-colors group"
95
+ style={{ paddingLeft: `${indent + 4}px` }}
96
+ >
97
+ {/* tree line connector */}
98
+ {depth > 0 && (
99
+ <span className="shrink-0 w-3 h-px bg-border" aria-hidden />
100
+ )}
101
+
102
+ <span
103
+ className={`shrink-0 flex items-center justify-center rounded p-1 ${
104
+ isLeaf ? 'bg-muted/60' : 'bg-primary/10'
105
+ }`}
106
+ >
107
+ <MenuIcon
108
+ name={node.icon}
109
+ className={`h-3.5 w-3.5 ${isLeaf ? 'text-muted-foreground' : 'text-primary'}`}
110
+ />
111
+ </span>
112
+
113
+ <div className="flex-1 min-w-0">
114
+ <span className="text-sm leading-tight truncate block">
115
+ {node.name}
116
+ </span>
117
+ {node.url && (
118
+ <span className="text-xs text-muted-foreground truncate block font-mono">
119
+ {node.url}
120
+ </span>
121
+ )}
122
+ {node.roles && node.roles.length > 0 && (
123
+ <div className="flex flex-wrap gap-1 mt-0.5">
124
+ {node.roles.map((role) => (
125
+ <span
126
+ key={role.slug}
127
+ className="inline-block rounded-full bg-muted px-1.5 py-0 text-[10px] font-medium text-muted-foreground leading-4"
128
+ title={role.slug}
129
+ >
130
+ {role.name || role.slug}
131
+ </span>
132
+ ))}
133
+ </div>
134
+ )}
135
+ </div>
136
+ </div>
137
+
138
+ {node.children.map((child) => (
139
+ <MenuTreeNode key={child.id} node={child} depth={depth + 1} />
140
+ ))}
141
+ </>
142
+ );
143
+ }
144
+
145
+ export function UserAccessSheet({
146
+ open,
147
+ onOpenChange,
148
+ userId,
149
+ userName,
150
+ }: UserAccessSheetProps) {
151
+ const t = useTranslations('core.UserPage');
152
+ const { request, currentLocaleCode } = useApp();
153
+
154
+ const { data: menus = [], isLoading: loadingMenus } = useQuery<UserMenu[]>({
155
+ queryKey: ['user-menus', userId, currentLocaleCode],
156
+ queryFn: async () => {
157
+ const res = await request<UserMenu[]>({
158
+ url: `/user/${userId}/menu`,
159
+ method: 'GET',
160
+ });
161
+ return res.data || [];
162
+ },
163
+ enabled: open && !!userId,
164
+ });
165
+
166
+ const { data: routes = [], isLoading: loadingRoutes } = useQuery<UserRoute[]>(
167
+ {
168
+ queryKey: ['user-routes', userId],
169
+ queryFn: async () => {
170
+ const res = await request<UserRoute[]>({
171
+ url: `/user/${userId}/route`,
172
+ method: 'GET',
173
+ });
174
+ return res.data || [];
175
+ },
176
+ enabled: open && !!userId,
177
+ }
178
+ );
179
+
180
+ const menuTree = buildTree(menus);
181
+
182
+ return (
183
+ <Sheet open={open} onOpenChange={onOpenChange}>
184
+ <ResizableSheetContent
185
+ sheetId="core-users-access-sheet"
186
+ defaultWidth={640}
187
+ minWidth={460}
188
+ maxWidth={980}
189
+ className="w-full sm:max-w-xl overflow-y-auto gap-0"
190
+ >
191
+ <SheetHeader className="px-6 pt-6 pb-4">
192
+ <SheetTitle className="flex items-center gap-2">
193
+ <Lock className="h-5 w-5 text-primary" />
194
+ {t('accessViewTitle')}
195
+ </SheetTitle>
196
+ <SheetDescription>
197
+ {userName
198
+ ? t('accessViewDescriptionUser', { name: userName })
199
+ : t('accessViewDescription')}
200
+ </SheetDescription>
201
+ </SheetHeader>
202
+
203
+ <div className="w-full border-t" />
204
+
205
+ <Tabs defaultValue="menu" className="px-6 pt-4">
206
+ <TabsList className="w-full mb-4">
207
+ <TabsTrigger
208
+ value="menu"
209
+ className="flex-1 flex items-center gap-2"
210
+ >
211
+ <Menu className="h-4 w-4" />
212
+ {t('accessTabMenu')}
213
+ {!loadingMenus && (
214
+ <Badge variant="secondary" className="ml-1 text-xs">
215
+ {menus.length}
216
+ </Badge>
217
+ )}
218
+ </TabsTrigger>
219
+ <TabsTrigger
220
+ value="routes"
221
+ className="flex-1 flex items-center gap-2"
222
+ >
223
+ <Globe className="h-4 w-4" />
224
+ {t('accessTabRoutes')}
225
+ {!loadingRoutes && (
226
+ <Badge variant="secondary" className="ml-1 text-xs">
227
+ {routes.length}
228
+ </Badge>
229
+ )}
230
+ </TabsTrigger>
231
+ </TabsList>
232
+
233
+ <TabsContent value="menu" className="mt-0">
234
+ {loadingMenus ? (
235
+ <div className="flex items-center justify-center py-12">
236
+ <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
237
+ <span className="ml-2 text-sm text-muted-foreground">
238
+ {t('loading')}
239
+ </span>
240
+ </div>
241
+ ) : menuTree.length === 0 ? (
242
+ <div className="flex flex-col items-center justify-center py-12 text-center">
243
+ <Menu className="h-10 w-10 text-muted-foreground/40 mb-3" />
244
+ <p className="text-sm text-muted-foreground">
245
+ {t('accessNoMenus')}
246
+ </p>
247
+ </div>
248
+ ) : (
249
+ <div className="rounded-lg border border-border/60 bg-card px-2 py-2">
250
+ {menuTree.map((node) => (
251
+ <MenuTreeNode key={node.id} node={node} depth={0} />
252
+ ))}
253
+ </div>
254
+ )}
255
+ </TabsContent>
256
+
257
+ <TabsContent value="routes" className="space-y-2 mt-0">
258
+ {loadingRoutes ? (
259
+ <div className="flex items-center justify-center py-12">
260
+ <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
261
+ <span className="ml-2 text-sm text-muted-foreground">
262
+ {t('loading')}
263
+ </span>
264
+ </div>
265
+ ) : routes.length === 0 ? (
266
+ <div className="flex flex-col items-center justify-center py-12 text-center">
267
+ <Globe className="h-10 w-10 text-muted-foreground/40 mb-3" />
268
+ <p className="text-sm text-muted-foreground">
269
+ {t('accessNoRoutes')}
270
+ </p>
271
+ </div>
272
+ ) : (
273
+ routes.map((route) => (
274
+ <div
275
+ key={route.id}
276
+ className="flex items-start gap-3 rounded-lg border border-border/60 bg-card px-4 py-2.5 hover:border-primary/40 transition-colors"
277
+ >
278
+ <span
279
+ className={`shrink-0 rounded border px-2 py-0.5 text-xs font-mono font-semibold ${
280
+ METHOD_COLORS[route.method] ??
281
+ 'bg-muted text-muted-foreground border-border'
282
+ }`}
283
+ >
284
+ {route.method}
285
+ </span>
286
+ <div className="flex-1 min-w-0">
287
+ <p className="text-sm font-mono truncate">{route.url}</p>
288
+ {route.roles && route.roles.length > 0 && (
289
+ <div className="flex flex-wrap gap-1 mt-0.5">
290
+ {route.roles.map((role) => (
291
+ <span
292
+ key={role.slug}
293
+ className="inline-block rounded-full bg-muted px-1.5 py-0 text-[10px] font-medium text-muted-foreground leading-4"
294
+ title={role.slug}
295
+ >
296
+ {role.name || role.slug}
297
+ </span>
298
+ ))}
299
+ </div>
300
+ )}
301
+ </div>
302
+ </div>
303
+ ))
304
+ )}
305
+ </TabsContent>
306
+ </Tabs>
307
+ </ResizableSheetContent>
308
+ </Sheet>
309
+ );
310
+ }