@hed-hog/core 0.0.355 → 0.0.358

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 (287) hide show
  1. package/dist/ai/ai.service.d.ts +3 -12
  2. package/dist/ai/ai.service.d.ts.map +1 -1
  3. package/dist/ai/ai.service.js +34 -125
  4. package/dist/ai/ai.service.js.map +1 -1
  5. package/dist/auth/guards/auth.guard.d.ts +6 -1
  6. package/dist/auth/guards/auth.guard.d.ts.map +1 -1
  7. package/dist/auth/guards/auth.guard.js +33 -2
  8. package/dist/auth/guards/auth.guard.js.map +1 -1
  9. package/dist/core.module.d.ts.map +1 -1
  10. package/dist/core.module.js +3 -3
  11. package/dist/core.module.js.map +1 -1
  12. package/dist/file/file.controller.d.ts +4 -2
  13. package/dist/file/file.controller.d.ts.map +1 -1
  14. package/dist/file/file.controller.js +44 -38
  15. package/dist/file/file.controller.js.map +1 -1
  16. package/dist/file/file.service.d.ts +2 -2
  17. package/dist/file/file.service.d.ts.map +1 -1
  18. package/dist/file/file.service.js +58 -89
  19. package/dist/file/file.service.js.map +1 -1
  20. package/dist/file/provider/azure.provider.d.ts.map +1 -1
  21. package/dist/file/provider/azure.provider.js +10 -15
  22. package/dist/file/provider/azure.provider.js.map +1 -1
  23. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  24. package/dist/file/provider/gcs.provider.js +8 -9
  25. package/dist/file/provider/gcs.provider.js.map +1 -1
  26. package/dist/file/provider/local.provider.d.ts.map +1 -1
  27. package/dist/file/provider/local.provider.js +11 -10
  28. package/dist/file/provider/local.provider.js.map +1 -1
  29. package/dist/file/provider/provider.enum.d.ts +2 -1
  30. package/dist/file/provider/provider.enum.d.ts.map +1 -1
  31. package/dist/file/provider/provider.enum.js +1 -0
  32. package/dist/file/provider/provider.enum.js.map +1 -1
  33. package/dist/file/provider/provider.factory.d.ts +1 -1
  34. package/dist/file/provider/provider.factory.d.ts.map +1 -1
  35. package/dist/file/provider/provider.factory.js +18 -5
  36. package/dist/file/provider/provider.factory.js.map +1 -1
  37. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  38. package/dist/file/provider/s3.provider.js +4 -20
  39. package/dist/file/provider/s3.provider.js.map +1 -1
  40. package/dist/file/provider/spaces.provider.d.ts +27 -0
  41. package/dist/file/provider/spaces.provider.d.ts.map +1 -0
  42. package/dist/file/provider/spaces.provider.js +203 -0
  43. package/dist/file/provider/spaces.provider.js.map +1 -0
  44. package/dist/index.d.ts +4 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +4 -3
  47. package/dist/index.js.map +1 -1
  48. package/dist/integration-profile/dto/create.dto.d.ts +9 -0
  49. package/dist/integration-profile/dto/create.dto.d.ts.map +1 -0
  50. package/dist/integration-profile/dto/create.dto.js +54 -0
  51. package/dist/integration-profile/dto/create.dto.js.map +1 -0
  52. package/dist/integration-profile/dto/export.dto.d.ts +5 -0
  53. package/dist/integration-profile/dto/export.dto.d.ts.map +1 -0
  54. package/dist/integration-profile/dto/export.dto.js +29 -0
  55. package/dist/integration-profile/dto/export.dto.js.map +1 -0
  56. package/dist/integration-profile/dto/import-confirm.dto.d.ts +17 -0
  57. package/dist/integration-profile/dto/import-confirm.dto.d.ts.map +1 -0
  58. package/dist/integration-profile/dto/import-confirm.dto.js +77 -0
  59. package/dist/integration-profile/dto/import-confirm.dto.js.map +1 -0
  60. package/dist/integration-profile/dto/update.dto.d.ts.map +1 -0
  61. package/dist/integration-profile/dto/update.dto.js.map +1 -0
  62. package/dist/integration-profile/integration-profile.controller.d.ts +142 -0
  63. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -0
  64. package/dist/integration-profile/integration-profile.controller.js +166 -0
  65. package/dist/integration-profile/integration-profile.controller.js.map +1 -0
  66. package/dist/integration-profile/integration-profile.module.d.ts +3 -0
  67. package/dist/integration-profile/integration-profile.module.d.ts.map +1 -0
  68. package/dist/{mail-profile/mail-profile.module.js → integration-profile/integration-profile.module.js} +11 -13
  69. package/dist/integration-profile/integration-profile.module.js.map +1 -0
  70. package/dist/integration-profile/integration-profile.service.d.ts +160 -0
  71. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -0
  72. package/dist/integration-profile/integration-profile.service.js +609 -0
  73. package/dist/integration-profile/integration-profile.service.js.map +1 -0
  74. package/dist/integration-profile/integration-profile.utils.d.ts +30 -0
  75. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -0
  76. package/dist/integration-profile/integration-profile.utils.js +156 -0
  77. package/dist/integration-profile/integration-profile.utils.js.map +1 -0
  78. package/dist/mail/mail.module.d.ts.map +1 -1
  79. package/dist/mail/mail.module.js +36 -23
  80. package/dist/mail/mail.module.js.map +1 -1
  81. package/dist/mail/mail.service.d.ts +4 -2
  82. package/dist/mail/mail.service.d.ts.map +1 -1
  83. package/dist/mail/mail.service.js +33 -51
  84. package/dist/mail/mail.service.js.map +1 -1
  85. package/dist/mcp/guards/mcp-auth.guard.js +1 -1
  86. package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
  87. package/dist/mcp/mcp-api-key.controller.d.ts +2 -0
  88. package/dist/mcp/mcp-api-key.controller.d.ts.map +1 -1
  89. package/dist/mcp/mcp-api-key.controller.js +1 -1
  90. package/dist/mcp/mcp-api-key.controller.js.map +1 -1
  91. package/dist/mcp/mcp-api-key.service.d.ts +2 -1
  92. package/dist/mcp/mcp-api-key.service.d.ts.map +1 -1
  93. package/dist/mcp/mcp-api-key.service.js +6 -2
  94. package/dist/mcp/mcp-api-key.service.js.map +1 -1
  95. package/dist/mcp/mcp.module.d.ts.map +1 -1
  96. package/dist/mcp/mcp.module.js +3 -5
  97. package/dist/mcp/mcp.module.js.map +1 -1
  98. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  99. package/dist/mcp-chat/mcp-chat.service.js +17 -16
  100. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  101. package/dist/oauth/interfaces/OAuthProvider.d.ts +1 -1
  102. package/dist/oauth/interfaces/OAuthProvider.d.ts.map +1 -1
  103. package/dist/oauth/oauth.controller.d.ts +4 -1
  104. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  105. package/dist/oauth/oauth.controller.js +31 -5
  106. package/dist/oauth/oauth.controller.js.map +1 -1
  107. package/dist/oauth/oauth.service.d.ts +12 -1
  108. package/dist/oauth/oauth.service.d.ts.map +1 -1
  109. package/dist/oauth/oauth.service.js +125 -23
  110. package/dist/oauth/oauth.service.js.map +1 -1
  111. package/dist/oauth/providers/abstract.provider.d.ts +1 -1
  112. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  113. package/dist/oauth/providers/facebook.provider.d.ts +5 -2
  114. package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
  115. package/dist/oauth/providers/facebook.provider.js +34 -19
  116. package/dist/oauth/providers/facebook.provider.js.map +1 -1
  117. package/dist/oauth/providers/github.provider.d.ts +6 -2
  118. package/dist/oauth/providers/github.provider.d.ts.map +1 -1
  119. package/dist/oauth/providers/github.provider.js +43 -20
  120. package/dist/oauth/providers/github.provider.js.map +1 -1
  121. package/dist/oauth/providers/google.provider.d.ts +5 -2
  122. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  123. package/dist/oauth/providers/google.provider.js +34 -19
  124. package/dist/oauth/providers/google.provider.js.map +1 -1
  125. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +5 -2
  126. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  127. package/dist/oauth/providers/microsoft-entra-id.provider.js +43 -40
  128. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  129. package/dist/oauth/providers/microsoft.provider.d.ts +5 -2
  130. package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
  131. package/dist/oauth/providers/microsoft.provider.js +34 -19
  132. package/dist/oauth/providers/microsoft.provider.js.map +1 -1
  133. package/dist/profile/profile.controller.js +1 -1
  134. package/dist/profile/profile.controller.js.map +1 -1
  135. package/dist/security/security.service.d.ts +2 -0
  136. package/dist/security/security.service.d.ts.map +1 -1
  137. package/dist/security/security.service.js +21 -0
  138. package/dist/security/security.service.js.map +1 -1
  139. package/dist/setting/setting.service.d.ts +26 -1
  140. package/dist/setting/setting.service.d.ts.map +1 -1
  141. package/dist/setting/setting.service.js +240 -7
  142. package/dist/setting/setting.service.js.map +1 -1
  143. package/dist/user/user.controller.d.ts +2 -1
  144. package/dist/user/user.controller.d.ts.map +1 -1
  145. package/dist/user/user.controller.js +5 -4
  146. package/dist/user/user.controller.js.map +1 -1
  147. package/dist/user/user.service.d.ts +4 -2
  148. package/dist/user/user.service.d.ts.map +1 -1
  149. package/dist/user/user.service.js +19 -4
  150. package/dist/user/user.service.js.map +1 -1
  151. package/dist/webhook-integration/dto/webhook-action.dto.d.ts +17 -2
  152. package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -1
  153. package/dist/webhook-integration/dto/webhook-action.dto.js +93 -2
  154. package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -1
  155. package/dist/webhook-integration/event-webhook.controller.d.ts +1 -0
  156. package/dist/webhook-integration/event-webhook.controller.d.ts.map +1 -1
  157. package/dist/webhook-integration/event-webhook.controller.js +10 -0
  158. package/dist/webhook-integration/event-webhook.controller.js.map +1 -1
  159. package/dist/webhook-integration/event-webhook.service.d.ts +2 -0
  160. package/dist/webhook-integration/event-webhook.service.d.ts.map +1 -1
  161. package/dist/webhook-integration/event-webhook.service.js +30 -0
  162. package/dist/webhook-integration/event-webhook.service.js.map +1 -1
  163. package/dist/webhook-integration/webhook-command.registry.d.ts +15 -0
  164. package/dist/webhook-integration/webhook-command.registry.d.ts.map +1 -0
  165. package/dist/webhook-integration/webhook-command.registry.js +36 -0
  166. package/dist/webhook-integration/webhook-command.registry.js.map +1 -0
  167. package/dist/webhook-integration/webhook-integration.controller.d.ts +20 -0
  168. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  169. package/dist/webhook-integration/webhook-integration.controller.js +48 -0
  170. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  171. package/dist/webhook-integration/webhook-integration.module.d.ts.map +1 -1
  172. package/dist/webhook-integration/webhook-integration.module.js +5 -0
  173. package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
  174. package/dist/webhook-integration/webhook-integration.service.d.ts +27 -2
  175. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  176. package/dist/webhook-integration/webhook-integration.service.js +377 -50
  177. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  178. package/hedhog/data/file_provider.yaml +9 -1
  179. package/hedhog/data/integration_provider.yaml +148 -0
  180. package/hedhog/data/integration_type.yaml +29 -0
  181. package/hedhog/data/menu.yaml +6 -21
  182. package/hedhog/data/role.yaml +8 -0
  183. package/hedhog/data/route.yaml +73 -373
  184. package/hedhog/data/setting_group.yaml +209 -365
  185. package/hedhog/data/setting_subgroup.yaml +21 -13
  186. package/hedhog/frontend/app/account/components/connected-accounts.tsx.ejs +25 -20
  187. package/hedhog/frontend/app/account/components/tokens.tsx.ejs +421 -0
  188. package/hedhog/frontend/app/account/layout.tsx.ejs +6 -3
  189. package/hedhog/frontend/app/account/mcp-tokens/page.tsx.ejs +2 -2
  190. package/hedhog/frontend/app/account/tokens/page.tsx.ejs +5 -0
  191. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +938 -225
  192. package/hedhog/frontend/app/configurations/layout.tsx.ejs +1 -1
  193. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +3 -1
  194. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +1830 -0
  195. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +829 -128
  196. package/hedhog/frontend/app/mail/profiles/page.tsx.ejs +1 -820
  197. package/hedhog/frontend/app/setting-list/page.tsx.ejs +229 -395
  198. package/hedhog/frontend/app/users/permissions.tsx.ejs +79 -2
  199. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +7 -2
  200. package/hedhog/frontend/messages/en.json +239 -3
  201. package/hedhog/frontend/messages/pt.json +239 -3
  202. package/hedhog/table/file.yaml +1 -1
  203. package/hedhog/table/integration_profile.yaml +34 -0
  204. package/hedhog/table/integration_provider.yaml +23 -0
  205. package/hedhog/table/integration_type.yaml +19 -0
  206. package/hedhog/table/mcp_api_key.yaml +4 -0
  207. package/hedhog/table/webhook_action.yaml +141 -85
  208. package/package.json +4 -3
  209. package/src/ai/ai.service.ts +35 -153
  210. package/src/auth/guards/auth.guard.ts +32 -0
  211. package/src/core.module.ts +3 -3
  212. package/src/file/file.controller.ts +51 -42
  213. package/src/file/file.service.ts +67 -104
  214. package/src/file/provider/azure.provider.ts +14 -28
  215. package/src/file/provider/gcs.provider.ts +9 -9
  216. package/src/file/provider/local.provider.ts +10 -13
  217. package/src/file/provider/provider.enum.ts +1 -0
  218. package/src/file/provider/provider.factory.ts +21 -6
  219. package/src/file/provider/s3.provider.ts +4 -20
  220. package/src/file/provider/spaces.provider.ts +246 -0
  221. package/src/index.ts +6 -3
  222. package/src/integration-profile/dto/create.dto.ts +44 -0
  223. package/src/integration-profile/dto/export.dto.ts +15 -0
  224. package/src/integration-profile/dto/import-confirm.dto.ts +61 -0
  225. package/src/integration-profile/integration-profile.controller.ts +131 -0
  226. package/src/integration-profile/integration-profile.module.ts +16 -0
  227. package/src/integration-profile/integration-profile.service.ts +793 -0
  228. package/src/integration-profile/integration-profile.utils.ts +173 -0
  229. package/src/mail/mail.module.ts +41 -22
  230. package/src/mail/mail.service.ts +40 -64
  231. package/src/mcp/guards/mcp-auth.guard.ts +1 -1
  232. package/src/mcp/mcp-api-key.controller.ts +2 -2
  233. package/src/mcp/mcp-api-key.service.ts +6 -2
  234. package/src/mcp/mcp.module.ts +3 -5
  235. package/src/mcp-chat/mcp-chat.service.ts +23 -20
  236. package/src/oauth/interfaces/OAuthProvider.ts +1 -1
  237. package/src/oauth/oauth.controller.ts +30 -2
  238. package/src/oauth/oauth.service.ts +194 -35
  239. package/src/oauth/providers/abstract.provider.ts +1 -1
  240. package/src/oauth/providers/facebook.provider.ts +36 -20
  241. package/src/oauth/providers/github.provider.ts +46 -23
  242. package/src/oauth/providers/google.provider.ts +36 -20
  243. package/src/oauth/providers/microsoft-entra-id.provider.ts +42 -46
  244. package/src/oauth/providers/microsoft.provider.ts +36 -21
  245. package/src/profile/profile.controller.ts +2 -2
  246. package/src/security/security.service.ts +34 -1
  247. package/src/setting/setting.service.ts +307 -9
  248. package/src/user/user.controller.ts +4 -1
  249. package/src/user/user.service.ts +20 -2
  250. package/src/webhook-integration/dto/webhook-action.dto.ts +85 -4
  251. package/src/webhook-integration/event-webhook.controller.ts +55 -50
  252. package/src/webhook-integration/event-webhook.service.ts +422 -384
  253. package/src/webhook-integration/webhook-command.registry.ts +42 -0
  254. package/src/webhook-integration/webhook-integration.controller.ts +25 -0
  255. package/src/webhook-integration/webhook-integration.module.ts +50 -45
  256. package/src/webhook-integration/webhook-integration.service.ts +444 -53
  257. package/dist/mail-profile/dto/create.dto.d.ts +0 -23
  258. package/dist/mail-profile/dto/create.dto.d.ts.map +0 -1
  259. package/dist/mail-profile/dto/create.dto.js +0 -139
  260. package/dist/mail-profile/dto/create.dto.js.map +0 -1
  261. package/dist/mail-profile/dto/update.dto.d.ts.map +0 -1
  262. package/dist/mail-profile/dto/update.dto.js.map +0 -1
  263. package/dist/mail-profile/mail-profile.controller.d.ts +0 -94
  264. package/dist/mail-profile/mail-profile.controller.d.ts.map +0 -1
  265. package/dist/mail-profile/mail-profile.controller.js +0 -95
  266. package/dist/mail-profile/mail-profile.controller.js.map +0 -1
  267. package/dist/mail-profile/mail-profile.module.d.ts +0 -3
  268. package/dist/mail-profile/mail-profile.module.d.ts.map +0 -1
  269. package/dist/mail-profile/mail-profile.module.js.map +0 -1
  270. package/dist/mail-profile/mail-profile.service.d.ts +0 -109
  271. package/dist/mail-profile/mail-profile.service.d.ts.map +0 -1
  272. package/dist/mail-profile/mail-profile.service.js +0 -278
  273. package/dist/mail-profile/mail-profile.service.js.map +0 -1
  274. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +0 -32
  275. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +0 -1
  276. package/dist/mcp/tools/core-mail-profile.mcp-tools.js +0 -178
  277. package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +0 -1
  278. package/hedhog/data/mail_profile.yaml +0 -18
  279. package/hedhog/table/mail_profile.yaml +0 -75
  280. package/src/mail-profile/dto/create.dto.ts +0 -145
  281. package/src/mail-profile/mail-profile.controller.ts +0 -56
  282. package/src/mail-profile/mail-profile.module.ts +0 -18
  283. package/src/mail-profile/mail-profile.service.ts +0 -308
  284. package/src/mcp/tools/core-mail-profile.mcp-tools.ts +0 -128
  285. /package/dist/{mail-profile → integration-profile}/dto/update.dto.d.ts +0 -0
  286. /package/dist/{mail-profile → integration-profile}/dto/update.dto.js +0 -0
  287. /package/src/{mail-profile → integration-profile}/dto/update.dto.ts +0 -0
@@ -1,54 +1,55 @@
1
1
  'use client';
2
2
 
3
- import {
4
- closestCenter,
5
- DndContext,
6
- type DragEndEvent,
7
- PointerSensor,
8
- useSensor,
9
- useSensors,
10
- } from '@dnd-kit/core';
11
- import {
12
- arrayMove,
13
- SortableContext,
14
- useSortable,
15
- verticalListSortingStrategy,
16
- } from '@dnd-kit/sortable';
17
- import { CSS } from '@dnd-kit/utilities';
18
3
  import {
19
4
  EmptyState,
5
+ EntityCard,
20
6
  Page,
21
7
  PageHeader,
8
+ PaginationFooter,
22
9
  SearchBar,
10
+ ViewModeToggle,
23
11
  } from '@/components/entity-list';
24
12
  import { Badge } from '@/components/ui/badge';
25
13
  import { Button } from '@/components/ui/button';
26
- import {
27
- Card,
28
- CardContent,
29
- CardDescription,
30
- CardHeader,
31
- CardTitle,
32
- } from '@/components/ui/card';
14
+ import { Card, CardContent } from '@/components/ui/card';
33
15
  import { Input } from '@/components/ui/input';
34
- import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
16
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
35
17
  import {
36
18
  Sheet,
37
19
  SheetDescription,
20
+ SheetFooter,
38
21
  SheetHeader,
39
22
  SheetTitle,
40
23
  } from '@/components/ui/sheet';
41
- import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
42
24
  import {
43
- Tooltip,
44
- TooltipContent,
45
- TooltipTrigger,
46
- } from '@/components/ui/tooltip';
25
+ Table,
26
+ TableBody,
27
+ TableCell,
28
+ TableHead,
29
+ TableHeader,
30
+ TableRow,
31
+ } from '@/components/ui/table';
32
+ import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
33
+ import { usePersistedViewMode } from '@/hooks/use-persisted-view-mode';
47
34
  import { PaginatedResult } from '@/types/pagination-result';
35
+ import {
36
+ closestCenter,
37
+ DndContext,
38
+ type DragEndEvent,
39
+ PointerSensor,
40
+ useSensor,
41
+ useSensors,
42
+ } from '@dnd-kit/core';
43
+ import {
44
+ arrayMove,
45
+ SortableContext,
46
+ useSortable,
47
+ verticalListSortingStrategy,
48
+ } from '@dnd-kit/sortable';
49
+ import { CSS } from '@dnd-kit/utilities';
48
50
  import {
49
51
  Setting,
50
52
  SettingComponentEnum,
51
- SettingGroup,
52
53
  SettingSubgroup,
53
54
  } from '@hed-hog/api-types';
54
55
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
@@ -57,9 +58,8 @@ import {
57
58
  ListTree,
58
59
  Pencil,
59
60
  Plus,
61
+ RefreshCw,
60
62
  Save,
61
- Settings2,
62
- SlidersHorizontal,
63
63
  Trash2,
64
64
  } from 'lucide-react';
65
65
  import { useTranslations } from 'next-intl';
@@ -75,14 +75,6 @@ type SettingWithList = Setting & {
75
75
  }>;
76
76
  };
77
77
 
78
- type SubgroupSection = {
79
- key: string;
80
- slug: string;
81
- name: string;
82
- description?: string;
83
- settings: SettingWithList[];
84
- };
85
-
86
78
  type DraftOption = {
87
79
  id: string;
88
80
  value: string;
@@ -102,8 +94,17 @@ export default function CoreSettingListPage() {
102
94
  const t = useTranslations('core.SettingListPage');
103
95
  const { request, currentLocaleCode, showToastHandler } = useApp();
104
96
  const [search, setSearch] = useState('');
105
- const [activeGroupSlug, setActiveGroupSlug] = useState<string>('all');
106
- const [activeSubgroupSlug, setActiveSubgroupSlug] = useState<string>('all');
97
+ const [page, setPage] = useState(1);
98
+ const [pageSize, setPageSize] = usePersistedPageSize({
99
+ storageKey: 'pagination:setting-list:pageSize',
100
+ defaultValue: 12,
101
+ allowedValues: [6, 12, 24, 48],
102
+ });
103
+ const [viewMode, setViewMode] = usePersistedViewMode({
104
+ storageKey: 'pagination:setting-list:viewMode',
105
+ defaultValue: 'list' as const,
106
+ allowedValues: ['list', 'cards'] as const,
107
+ });
107
108
  const [savingOptionBySettingId, setSavingOptionBySettingId] = useState<
108
109
  Record<number, boolean>
109
110
  >({});
@@ -123,57 +124,15 @@ export default function CoreSettingListPage() {
123
124
  })
124
125
  );
125
126
 
126
- const { data: groupsPage } = useQuery<PaginatedResult<SettingGroup>>({
127
- queryKey: ['core-setting-list-groups', currentLocaleCode],
128
- queryFn: async () => {
129
- const response = await request<PaginatedResult<SettingGroup>>({
130
- url: '/setting/group?page=1&pageSize=100',
131
- method: 'GET',
132
- });
133
- return response.data;
134
- },
135
- placeholderData: (previous) =>
136
- previous ?? createEmptyPage<SettingGroup>(100),
137
- });
138
-
139
- const groups = useMemo<SettingGroup[]>(
140
- () => groupsPage?.data ?? [],
141
- [groupsPage]
142
- );
143
-
144
- useEffect(() => {
145
- if (!groups.length) return;
146
-
147
- if (activeGroupSlug === 'all') {
148
- return;
149
- }
150
-
151
- const groupExists = groups.some(
152
- (group: SettingGroup) => group.slug === activeGroupSlug
153
- );
154
- if (!groupExists) {
155
- setActiveGroupSlug('all');
156
- }
157
- }, [activeGroupSlug, groups]);
158
-
159
- useEffect(() => {
160
- setActiveSubgroupSlug('all');
161
- }, [activeGroupSlug]);
162
-
163
127
  const {
164
128
  data: settingsPage,
165
129
  isLoading,
166
130
  refetch,
167
131
  } = useQuery<PaginatedResult<SettingWithList>>({
168
- queryKey: ['core-setting-list-items', activeGroupSlug, currentLocaleCode],
132
+ queryKey: ['core-setting-list-items', currentLocaleCode],
169
133
  queryFn: async () => {
170
- const url =
171
- activeGroupSlug === 'all'
172
- ? '/setting?page=1&pageSize=500'
173
- : `/setting/group/${activeGroupSlug}?page=1&pageSize=200`;
174
-
175
134
  const response = await request<PaginatedResult<SettingWithList>>({
176
- url,
135
+ url: '/setting?page=1&pageSize=500',
177
136
  method: 'GET',
178
137
  });
179
138
  return response.data;
@@ -191,62 +150,22 @@ export default function CoreSettingListPage() {
191
150
  () =>
192
151
  allSettings.filter((setting) => {
193
152
  const component = setting.component;
194
- const isListComponent =
153
+ return (
195
154
  component === SettingComponentEnum.COMBOBOX ||
196
- component === SettingComponentEnum.CHECKBOX;
197
-
198
- return isListComponent;
155
+ component === SettingComponentEnum.CHECKBOX
156
+ );
199
157
  }),
200
158
  [allSettings]
201
159
  );
202
160
 
203
- const subgroupOptions = useMemo(() => {
204
- const map = new Map<
205
- string,
206
- { value: string; label: string; description?: string }
207
- >();
208
-
209
- listTypeSettings.forEach((setting) => {
210
- const subgroup = setting.setting_subgroup;
211
- if (!subgroup?.slug) {
212
- return;
213
- }
214
-
215
- map.set(subgroup.slug, {
216
- value: subgroup.slug,
217
- label: subgroup.name?.toString() || subgroup.slug,
218
- description: subgroup.description?.toString() || undefined,
219
- });
220
- });
221
-
222
- return Array.from(map.values()).sort((left, right) =>
223
- left.label.localeCompare(right.label)
224
- );
225
- }, [listTypeSettings]);
226
-
227
- const visibleSettings = useMemo(() => {
228
- if (activeSubgroupSlug === 'all') {
229
- return listTypeSettings;
230
- }
231
-
232
- return listTypeSettings.filter(
233
- (setting) => setting.setting_subgroup?.slug === activeSubgroupSlug
234
- );
235
- }, [activeSubgroupSlug, listTypeSettings]);
236
-
237
- const currentGroup = useMemo(
238
- () => groups.find((group) => group.slug === activeGroupSlug) ?? null,
239
- [activeGroupSlug, groups]
240
- );
241
-
242
161
  const filteredSettings = useMemo(() => {
243
162
  const normalizedQuery = search.trim().toLowerCase();
244
163
 
245
164
  if (!normalizedQuery) {
246
- return visibleSettings;
165
+ return listTypeSettings;
247
166
  }
248
167
 
249
- return visibleSettings.filter((setting: SettingWithList) => {
168
+ return listTypeSettings.filter((setting: SettingWithList) => {
250
169
  const options = (setting.setting_list ?? []).map(
251
170
  (option: { value: string }) => option.value
252
171
  );
@@ -264,94 +183,15 @@ export default function CoreSettingListPage() {
264
183
 
265
184
  return text.includes(normalizedQuery);
266
185
  });
267
- }, [search, visibleSettings]);
268
-
269
- const groupedSections = useMemo<SubgroupSection[]>(() => {
270
- const sectionsMap = new Map<string, SubgroupSection>();
271
-
272
- filteredSettings.forEach((setting) => {
273
- const subgroup = setting.setting_subgroup ?? null;
274
- const subgroupSlug = subgroup?.slug ?? '__ungrouped__';
275
- const sectionKey = subgroupSlug;
276
-
277
- if (!sectionsMap.has(sectionKey)) {
278
- sectionsMap.set(sectionKey, {
279
- key: sectionKey,
280
- slug: subgroupSlug,
281
- name:
282
- subgroup?.name?.toString() ||
283
- (subgroup ? subgroup.slug : t('sections.withoutSubgroup')),
284
- description: subgroup?.description?.toString() || undefined,
285
- settings: [],
286
- });
287
- }
288
-
289
- sectionsMap.get(sectionKey)?.settings.push(setting);
290
- });
186
+ }, [search, listTypeSettings]);
291
187
 
292
- const sections = Array.from(sectionsMap.values());
293
- sections.forEach((section) => {
294
- section.settings.sort((left, right) =>
295
- left.slug.localeCompare(right.slug)
296
- );
297
- });
298
-
299
- sections.sort((left, right) => {
300
- if (left.slug === '__ungrouped__') return 1;
301
- if (right.slug === '__ungrouped__') return -1;
302
- return left.name.localeCompare(right.name);
303
- });
304
-
305
- return sections;
306
- }, [filteredSettings, t]);
188
+ useEffect(() => {
189
+ setPage(1);
190
+ }, [search]);
307
191
 
308
- const settingsWithOptionsCount = useMemo(
309
- () => listTypeSettings.length,
310
- [listTypeSettings]
311
- );
312
-
313
- const totalOptions = useMemo(
314
- () =>
315
- listTypeSettings.reduce(
316
- (acc: number, setting: SettingWithList) =>
317
- acc + (setting.setting_list?.length ?? 0),
318
- 0
319
- ),
320
- [listTypeSettings]
321
- );
322
-
323
- const controls = useMemo(
324
- () => [
325
- {
326
- id: 'group',
327
- type: 'select' as const,
328
- value: activeGroupSlug,
329
- onChange: (value: string) => setActiveGroupSlug(value),
330
- placeholder: t('filters.groupPlaceholder'),
331
- options: [
332
- { value: 'all', label: t('filters.allGroups') },
333
- ...groups.map((group: SettingGroup) => ({
334
- value: group.slug,
335
- label: group.name || group.slug,
336
- })),
337
- ],
338
- },
339
- {
340
- id: 'subgroup',
341
- type: 'select' as const,
342
- value: activeSubgroupSlug,
343
- onChange: (value: string) => setActiveSubgroupSlug(value),
344
- placeholder: t('filters.subgroupPlaceholder'),
345
- options: [
346
- { value: 'all', label: t('filters.allSubgroups') },
347
- ...subgroupOptions.map((subgroup) => ({
348
- value: subgroup.value,
349
- label: subgroup.label,
350
- })),
351
- ],
352
- },
353
- ],
354
- [activeGroupSlug, activeSubgroupSlug, groups, subgroupOptions, t]
192
+ const paginatedSettings = useMemo(
193
+ () => filteredSettings.slice((page - 1) * pageSize, page * pageSize),
194
+ [filteredSettings, page, pageSize]
355
195
  );
356
196
 
357
197
  const editingSetting = useMemo(
@@ -399,13 +239,7 @@ export default function CoreSettingListPage() {
399
239
 
400
240
  const saveSettingOptions = async (
401
241
  setting: SettingWithList,
402
- overrideValues?: string[],
403
- successMessageKey:
404
- | 'optionsSavedSuccess'
405
- | 'optionsClearedSuccess' = 'optionsSavedSuccess',
406
- errorMessageKey:
407
- | 'optionsSavedError'
408
- | 'optionsClearedError' = 'optionsSavedError'
242
+ overrideValues?: string[]
409
243
  ) => {
410
244
  const settingId = setting.id;
411
245
  if (!settingId) {
@@ -431,10 +265,10 @@ export default function CoreSettingListPage() {
431
265
  },
432
266
  });
433
267
 
434
- showToastHandler('success', t(successMessageKey));
268
+ showToastHandler('success', t('optionsSavedSuccess'));
435
269
  await refetch();
436
270
  } catch (_error) {
437
- showToastHandler('error', t(errorMessageKey));
271
+ showToastHandler('error', t('optionsSavedError'));
438
272
  } finally {
439
273
  setSavingOptionBySettingId((previous) => ({
440
274
  ...previous,
@@ -454,15 +288,6 @@ export default function CoreSettingListPage() {
454
288
  setEditingSettingId(null);
455
289
  };
456
290
 
457
- const handleClearOptions = async (setting: SettingWithList) => {
458
- await saveSettingOptions(
459
- setting,
460
- [],
461
- 'optionsClearedSuccess',
462
- 'optionsClearedError'
463
- );
464
- };
465
-
466
291
  const addSheetOption = () => {
467
292
  setSheetDraftOptions((previous) => [...previous, createDraftOption('')]);
468
293
  };
@@ -491,74 +316,29 @@ export default function CoreSettingListPage() {
491
316
  ]}
492
317
  title={t('title')}
493
318
  description={t('description')}
494
- />
495
-
496
- <KpiCardsGrid
497
- items={[
319
+ actions={[
498
320
  {
499
- key: 'groups',
500
- title: t('kpis.groups'),
501
- value: groups.length,
502
- icon: Settings2,
503
- iconContainerClassName: 'bg-sky-50 text-sky-700',
504
- accentClassName: 'from-sky-500/20 via-sky-500/10 to-transparent',
505
- },
506
- {
507
- key: 'settings',
508
- title: t('kpis.settingsTotal'),
509
- value: settingsWithOptionsCount,
510
- icon: ListTree,
511
- iconContainerClassName: 'bg-emerald-50 text-emerald-700',
512
- accentClassName:
513
- 'from-emerald-500/20 via-emerald-500/10 to-transparent',
514
- },
515
- {
516
- key: 'subgroups',
517
- title: t('kpis.subgroups'),
518
- value: subgroupOptions.length,
519
- icon: SlidersHorizontal,
520
- iconContainerClassName: 'bg-amber-50 text-amber-700',
521
- accentClassName:
522
- 'from-amber-500/20 via-amber-500/10 to-transparent',
523
- },
524
- {
525
- key: 'options',
526
- title: t('kpis.totalOptions'),
527
- value: totalOptions,
528
- icon: SlidersHorizontal,
529
- iconContainerClassName: 'bg-violet-50 text-violet-700',
530
- accentClassName:
531
- 'from-violet-500/20 via-violet-500/10 to-transparent',
321
+ label: t('actions.refresh'),
322
+ onClick: () => refetch(),
323
+ icon: <RefreshCw className="h-4 w-4" />,
324
+ iconOnly: true,
532
325
  },
533
326
  ]}
534
327
  />
535
328
 
536
- <Card>
537
- <CardContent className="flex flex-wrap items-center justify-between gap-3 py-3">
538
- <div className="space-y-0.5">
539
- <p className="text-sm font-medium text-foreground">
540
- {activeGroupSlug === 'all'
541
- ? t('filters.allGroups')
542
- : currentGroup?.name || activeGroupSlug}
543
- </p>
544
- {activeGroupSlug !== 'all' && currentGroup?.description ? (
545
- <p className="text-xs text-muted-foreground">
546
- {currentGroup.description}
547
- </p>
548
- ) : null}
549
- </div>
550
- <Badge variant="secondary">
551
- {t('kpis.options')}: {totalOptions}
552
- </Badge>
553
- </CardContent>
554
- </Card>
555
-
556
329
  <SearchBar
557
330
  searchQuery={search}
558
- onSearchChange={setSearch}
559
- onSearch={() => undefined}
331
+ onSearchChange={(value) => setSearch(value)}
560
332
  placeholder={t('searchPlaceholder')}
561
- controls={controls}
333
+ showSearchButton={false}
334
+ actions={
335
+ <ViewModeToggle
336
+ viewMode={viewMode}
337
+ onViewModeChange={setViewMode}
338
+ listLabel={t('viewMode.list')}
339
+ cardsLabel={t('viewMode.cards')}
340
+ />
341
+ }
562
342
  />
563
343
 
564
344
  {isLoading ? (
@@ -577,87 +357,137 @@ export default function CoreSettingListPage() {
577
357
  : t('emptyDescriptionFiltered')
578
358
  }
579
359
  />
360
+ ) : viewMode === 'list' ? (
361
+ <div className="rounded-md border">
362
+ <Table>
363
+ <TableHeader>
364
+ <TableRow>
365
+ <TableHead>{t('columns.name')}</TableHead>
366
+ <TableHead>{t('columns.group')}</TableHead>
367
+ <TableHead>{t('columns.type')}</TableHead>
368
+ <TableHead className="text-right">
369
+ {t('columns.items')}
370
+ </TableHead>
371
+ <TableHead className="w-30" />
372
+ </TableRow>
373
+ </TableHeader>
374
+ <TableBody>
375
+ {paginatedSettings.map((setting: SettingWithList) => {
376
+ const isSaving =
377
+ setting.id != null
378
+ ? savingOptionBySettingId[setting.id] === true
379
+ : false;
380
+
381
+ return (
382
+ <TableRow
383
+ key={setting.id}
384
+ className="cursor-pointer"
385
+ onDoubleClick={() => openSheetForSetting(setting)}
386
+ >
387
+ <TableCell>
388
+ <p className="font-medium">
389
+ {String(setting.name ?? setting.slug)}
390
+ </p>
391
+ <p className="text-xs text-muted-foreground">
392
+ {setting.slug}
393
+ </p>
394
+ </TableCell>
395
+ <TableCell className="text-muted-foreground">
396
+ {setting.setting_group
397
+ ? String(
398
+ setting.setting_group.name ??
399
+ setting.setting_group.slug
400
+ )
401
+ : '—'}
402
+ </TableCell>
403
+ <TableCell>
404
+ <Badge variant="outline">{setting.component}</Badge>
405
+ </TableCell>
406
+ <TableCell className="text-right">
407
+ <Badge variant="secondary">
408
+ {t('itemCount', {
409
+ count: setting.setting_list?.length ?? 0,
410
+ })}
411
+ </Badge>
412
+ </TableCell>
413
+ <TableCell>
414
+ <Button
415
+ size="sm"
416
+ variant="outline"
417
+ onClick={() => openSheetForSetting(setting)}
418
+ disabled={setting.id == null || isSaving}
419
+ >
420
+ <Pencil className="mr-1 h-3.5 w-3.5" />
421
+ {t('actions.editOptions')}
422
+ </Button>
423
+ </TableCell>
424
+ </TableRow>
425
+ );
426
+ })}
427
+ </TableBody>
428
+ </Table>
429
+ </div>
580
430
  ) : (
581
- <div className="grid gap-3">
582
- {groupedSections.map((section) => (
583
- <Card key={section.key}>
584
- <CardHeader className="pb-3">
585
- <CardTitle className="text-base">{section.name}</CardTitle>
586
- {section.description ? (
587
- <CardDescription>{section.description}</CardDescription>
588
- ) : null}
589
- </CardHeader>
590
-
591
- <CardContent className="space-y-4">
592
- {section.settings.map((setting: SettingWithList) => {
593
- const settingId = setting.id;
594
- const isSaving =
595
- settingId != null
596
- ? savingOptionBySettingId[settingId] === true
597
- : false;
598
- const optionsCount = setting.setting_list?.length ?? 0;
599
-
600
- return (
601
- <div
602
- key={setting.id}
603
- className="flex items-center justify-between gap-3 border-b border-border/60 pb-3 last:border-0 last:pb-0"
604
- >
605
- <div className="min-w-0 flex-1">
606
- <p className="truncate text-sm font-semibold text-foreground">
607
- {setting.name}
608
- </p>
609
- </div>
610
-
611
- <div className="flex items-center gap-2">
612
- <Badge variant="secondary">
613
- {t('itemCount', { count: optionsCount })}
614
- </Badge>
615
-
616
- <Tooltip>
617
- <TooltipTrigger asChild>
618
- <Button
619
- type="button"
620
- size="icon"
621
- variant="ghost"
622
- onClick={() => openSheetForSetting(setting)}
623
- disabled={settingId == null || isSaving}
624
- aria-label={t('actions.editOptions')}
625
- >
626
- <Pencil className="h-4 w-4" />
627
- </Button>
628
- </TooltipTrigger>
629
- <TooltipContent>
630
- <p>{t('actions.editOptions')}</p>
631
- </TooltipContent>
632
- </Tooltip>
633
-
634
- <Tooltip>
635
- <TooltipTrigger asChild>
636
- <Button
637
- type="button"
638
- size="icon"
639
- variant="ghost"
640
- onClick={() => handleClearOptions(setting)}
641
- disabled={settingId == null || isSaving}
642
- aria-label={t('actions.clearOptions')}
643
- >
644
- <Trash2 className="h-4 w-4" />
645
- </Button>
646
- </TooltipTrigger>
647
- <TooltipContent>
648
- <p>{t('actions.clearOptions')}</p>
649
- </TooltipContent>
650
- </Tooltip>
651
- </div>
652
- </div>
653
- );
654
- })}
655
- </CardContent>
656
- </Card>
431
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
432
+ {paginatedSettings.map((setting: SettingWithList) => (
433
+ <div
434
+ key={setting.id}
435
+ onDoubleClick={() => openSheetForSetting(setting)}
436
+ >
437
+ <EntityCard
438
+ title={String(setting.name ?? setting.slug)}
439
+ description={setting.slug}
440
+ badges={[
441
+ ...(setting.setting_group
442
+ ? [
443
+ {
444
+ label: String(
445
+ setting.setting_group.name ??
446
+ setting.setting_group.slug
447
+ ),
448
+ variant: 'secondary' as const,
449
+ },
450
+ ]
451
+ : []),
452
+ {
453
+ label: String(setting.component ?? ''),
454
+ variant: 'outline' as const,
455
+ },
456
+ ]}
457
+ metadata={[
458
+ {
459
+ label: t('columns.items'),
460
+ value: String(setting.setting_list?.length ?? 0),
461
+ },
462
+ ]}
463
+ actions={[
464
+ {
465
+ label: t('actions.editOptions'),
466
+ onClick: () => openSheetForSetting(setting),
467
+ variant: 'outline',
468
+ icon: <Pencil className="h-3.5 w-3.5" />,
469
+ },
470
+ ]}
471
+ />
472
+ </div>
657
473
  ))}
658
474
  </div>
659
475
  )}
660
476
 
477
+ {!isLoading && filteredSettings.length > 0 && (
478
+ <PaginationFooter
479
+ currentPage={page}
480
+ pageSize={pageSize}
481
+ totalItems={filteredSettings.length}
482
+ onPageChange={setPage}
483
+ onPageSizeChange={(size) => {
484
+ setPageSize(size);
485
+ setPage(1);
486
+ }}
487
+ pageSizeOptions={[6, 12, 24, 48]}
488
+ />
489
+ )}
490
+
661
491
  <Sheet
662
492
  open={sheetOpen}
663
493
  onOpenChange={(open) => {
@@ -670,13 +500,17 @@ export default function CoreSettingListPage() {
670
500
  >
671
501
  <ResizableSheetContent
672
502
  sheetId="core-setting-list-options-sheet"
673
- defaultWidth={768}
674
- minWidth={520}
675
- maxWidth={1100}
676
- className="w-full overflow-y-auto sm:max-w-2xl"
503
+ defaultWidth={560}
504
+ minWidth={400}
505
+ maxWidth={900}
506
+ className="flex w-full flex-col overflow-hidden"
677
507
  >
678
- <SheetHeader>
679
- <SheetTitle>{t('sheet.title')}</SheetTitle>
508
+ <SheetHeader className="shrink-0">
509
+ <SheetTitle>
510
+ {editingSetting
511
+ ? String(editingSetting.name ?? editingSetting.slug)
512
+ : t('sheet.title')}
513
+ </SheetTitle>
680
514
  <SheetDescription>
681
515
  {editingSetting
682
516
  ? t('sheet.description', { name: editingSetting.name ?? '' })
@@ -684,7 +518,7 @@ export default function CoreSettingListPage() {
684
518
  </SheetDescription>
685
519
  </SheetHeader>
686
520
 
687
- <div className="mt-6 space-y-4">
521
+ <div className="mt-6 min-h-0 flex-1 space-y-4 overflow-y-auto px-4">
688
522
  <div className="flex items-center justify-between gap-2">
689
523
  <Badge variant="secondary">
690
524
  {t('itemCount', { count: sheetDraftOptions.length })}
@@ -730,25 +564,25 @@ export default function CoreSettingListPage() {
730
564
  </SortableContext>
731
565
  </DndContext>
732
566
  )}
733
-
734
- <div className="flex justify-end">
735
- <Button
736
- type="button"
737
- onClick={handleSaveSheet}
738
- disabled={
739
- editingSettingId == null ||
740
- (editingSettingId != null &&
741
- savingOptionBySettingId[editingSettingId] === true)
742
- }
743
- >
744
- <Save className="mr-1 h-3.5 w-3.5" />
745
- {editingSettingId != null &&
746
- savingOptionBySettingId[editingSettingId]
747
- ? t('actions.saving')
748
- : t('actions.saveOptions')}
749
- </Button>
750
- </div>
751
567
  </div>
568
+
569
+ <SheetFooter className="mt-6 shrink-0 px-4 pb-4">
570
+ <Button
571
+ type="button"
572
+ onClick={handleSaveSheet}
573
+ disabled={
574
+ editingSettingId == null ||
575
+ (editingSettingId != null &&
576
+ savingOptionBySettingId[editingSettingId] === true)
577
+ }
578
+ >
579
+ <Save className="mr-1 h-3.5 w-3.5" />
580
+ {editingSettingId != null &&
581
+ savingOptionBySettingId[editingSettingId]
582
+ ? t('actions.saving')
583
+ : t('actions.saveOptions')}
584
+ </Button>
585
+ </SheetFooter>
752
586
  </ResizableSheetContent>
753
587
  </Sheet>
754
588
  </Page>