@allbluecn/admin-app 0.3.2 → 0.3.4

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 (209) hide show
  1. package/allblue-ui/package.json +6 -0
  2. package/allblue-ui/src/components/confirm-dialog.tsx +94 -0
  3. package/allblue-ui/src/components/infinite-scroll-sentinel.tsx +37 -0
  4. package/allblue-ui/src/components/notion-avatar.tsx +126 -0
  5. package/allblue-ui/src/components/user-avatar.tsx +82 -0
  6. package/{src/components → allblue-ui/src}/data-table/data-table-pagination.tsx +14 -5
  7. package/{src/components → allblue-ui/src}/data-table/data-table.tsx +1 -1
  8. package/{src/components → allblue-ui/src}/data-table/table-detail-layout.tsx +5 -6
  9. package/allblue-ui/src/hooks/use-load-more.ts +57 -0
  10. package/allblue-ui/src/index.ts +31 -0
  11. package/allblue-ui/src/lib/datetime.ts +226 -0
  12. package/allblue-ui/src/lib/sanitize.ts +18 -0
  13. package/allblue-ui/src/lib/utils.ts +1 -0
  14. package/{src/components → allblue-ui/src}/ui/alert-dialog.tsx +1 -3
  15. package/{src/components → allblue-ui/src}/ui/checkbox.tsx +0 -1
  16. package/{src/components → allblue-ui/src}/ui/dialog.tsx +1 -1
  17. package/{src/components → allblue-ui/src}/ui/dropdown-menu.tsx +0 -2
  18. package/{src/components → allblue-ui/src}/ui/field.tsx +2 -2
  19. package/{src/components → allblue-ui/src}/ui/input-group.tsx +3 -4
  20. package/allblue-ui/src/ui/input.tsx +19 -0
  21. package/{src/components → allblue-ui/src}/ui/pagination.tsx +1 -1
  22. package/{src/components → allblue-ui/src}/ui/select.tsx +0 -2
  23. package/{src/components → allblue-ui/src}/ui/separator.tsx +1 -0
  24. package/{src/components → allblue-ui/src}/ui/sheet.tsx +1 -2
  25. package/{src/components → allblue-ui/src}/ui/sidebar.tsx +7 -7
  26. package/{src/components → allblue-ui/src}/ui/switch.tsx +1 -0
  27. package/{src/components → allblue-ui/src}/ui/table.tsx +0 -2
  28. package/{src/components → allblue-ui/src}/ui/toast.tsx +44 -18
  29. package/{src/components → allblue-ui/src}/ui/tooltip.tsx +1 -0
  30. package/package.json +21 -18
  31. package/src/components/admin/admin-user-menu.tsx +4 -4
  32. package/src/components/admin/developer-log.tsx +16 -32
  33. package/src/components/ai-memory/memory-facts-panel.tsx +30 -70
  34. package/src/components/ai-prompts/preview-panel.tsx +1 -1
  35. package/src/components/ai-prompts/prompt-editor.tsx +8 -9
  36. package/src/components/ai-prompts/prompt-table.tsx +6 -6
  37. package/src/components/ai-prompts/section-editor.tsx +3 -3
  38. package/src/components/ai-prompts/variable-panel.tsx +2 -2
  39. package/src/components/ai-prompts/version-history.tsx +21 -41
  40. package/src/components/ai-provider/model-list-editor.tsx +2 -3
  41. package/src/components/ai-provider/provider-form.tsx +7 -7
  42. package/src/components/ai-provider/provider-table.tsx +17 -40
  43. package/src/components/auth/admin-login-form.tsx +7 -7
  44. package/src/components/auth/icon-input.tsx +2 -2
  45. package/src/components/auth/password-input.tsx +2 -2
  46. package/src/components/component/component-form-dialog.tsx +10 -18
  47. package/src/components/component/component-import-dialog.tsx +6 -6
  48. package/src/components/component/component-preview.tsx +311 -0
  49. package/src/components/component/component-table.tsx +3 -3
  50. package/src/components/dependency/changelog-dialog.tsx +2 -2
  51. package/src/components/dependency/columns.tsx +3 -3
  52. package/src/components/dependency/dependency-table.tsx +7 -7
  53. package/src/components/dependency/edit-dialog.tsx +8 -8
  54. package/src/components/invitations/invitations-page.tsx +26 -49
  55. package/src/components/knowledge-public/bookmark-form-dialog.tsx +6 -6
  56. package/src/components/knowledge-public/compliance-form-dialog.tsx +6 -6
  57. package/src/components/knowledge-public/legal-form-dialog.tsx +7 -7
  58. package/src/components/layout/sidebar-02/app-sidebar.tsx +1 -2
  59. package/src/components/layout/sidebar-02/index.tsx +1 -1
  60. package/src/components/layout/sidebar-02/nav-main.tsx +2 -3
  61. package/src/components/layout/sidebar-02/nav-notifications.tsx +3 -3
  62. package/src/components/market/columns.tsx +4 -4
  63. package/src/components/market/market-app-form.tsx +6 -7
  64. package/src/components/market/market-app-list.tsx +14 -29
  65. package/src/components/monitoring/error-list.tsx +2 -2
  66. package/src/components/monitoring/metrics-stats.tsx +1 -1
  67. package/src/components/monitoring/request-log-table.tsx +2 -2
  68. package/src/components/oauth-providers/provider-page.tsx +20 -38
  69. package/src/components/permissions/assign-dialog.tsx +135 -0
  70. package/src/components/permissions/create-role-dialog.tsx +135 -0
  71. package/src/components/permissions/types.ts +49 -0
  72. package/src/components/user/columns.tsx +6 -6
  73. package/src/components/user/settings-activity-log.tsx +7 -7
  74. package/src/components/user/settings-profile/avatar-uploader.tsx +118 -0
  75. package/src/components/user/settings-profile/social-links.tsx +63 -0
  76. package/src/components/user/settings-profile.tsx +26 -125
  77. package/src/components/user/user-create-dialog.tsx +6 -6
  78. package/src/components/user/user-detail/error-boundary.tsx +19 -0
  79. package/src/components/user/user-detail/user-detail-dialogs.tsx +203 -0
  80. package/src/components/user/user-filters.tsx +4 -4
  81. package/src/lib/auth/page-gate.ts +28 -0
  82. package/src/lib/auth.config.ts +25 -9
  83. package/src/lib/i18n/detect.ts +1 -1
  84. package/src/lib/loader-serial.ts +14 -0
  85. package/src/providers/index.tsx +1 -1
  86. package/src/routeTree.gen.ts +67 -78
  87. package/src/routes/__root.tsx +4 -4
  88. package/src/routes/_nav/audit/route.lazy.tsx +1 -2
  89. package/src/routes/_nav/compliance/route.lazy.tsx +5 -3
  90. package/src/routes/_nav/components/route.lazy.tsx +14 -34
  91. package/src/routes/_nav/knowledge-public/bookmarks/route.lazy.tsx +18 -38
  92. package/src/routes/_nav/knowledge-public/compliance/route.lazy.tsx +18 -38
  93. package/src/routes/_nav/knowledge-public/legal/route.lazy.tsx +6 -6
  94. package/src/routes/_nav/licenses/$licenseId/route.lazy.tsx +3 -3
  95. package/src/routes/_nav/licenses/new/route.lazy.tsx +5 -5
  96. package/src/routes/_nav/licenses/route.lazy.tsx +1 -1
  97. package/src/routes/_nav/monitoring/ai-usage/route.lazy.tsx +2 -2
  98. package/src/routes/_nav/monitoring/errors/route.lazy.tsx +10 -10
  99. package/src/routes/_nav/monitoring/errors/route.tsx +7 -0
  100. package/src/routes/_nav/monitoring/health/route.lazy.tsx +13 -15
  101. package/src/routes/_nav/monitoring/health/route.tsx +14 -0
  102. package/src/routes/_nav/monitoring/logs/route.lazy.tsx +24 -9
  103. package/src/routes/_nav/monitoring/logs/route.tsx +13 -0
  104. package/src/routes/_nav/monitoring/metrics/route.lazy.tsx +12 -14
  105. package/src/routes/_nav/monitoring/metrics/route.tsx +7 -0
  106. package/src/routes/_nav/monitoring/overview/route.lazy.tsx +14 -13
  107. package/src/routes/_nav/monitoring/overview/route.tsx +17 -0
  108. package/src/routes/_nav/monitoring/route.lazy.tsx +6 -6
  109. package/src/routes/_nav/monitoring/sentry/route.lazy.tsx +2 -2
  110. package/src/routes/_nav/permissions/route.lazy.tsx +21 -311
  111. package/src/routes/_nav/permissions/route.tsx +1 -1
  112. package/src/routes/_nav/route.tsx +5 -13
  113. package/src/routes/_nav/sso/route.lazy.tsx +2 -3
  114. package/src/routes/_nav/users/$userId/route.lazy.tsx +91 -156
  115. package/src/routes/_nav/users/-components/users-table.tsx +1 -1
  116. package/src/routes/_nav/users/route.lazy.tsx +1 -1
  117. package/src/routes/login/route.lazy.tsx +1 -1
  118. package/src/routes/setup/route.lazy.tsx +2 -2
  119. package/src/server/__tests__/admin-billing.test.ts +191 -16
  120. package/src/server/__tests__/compliance.test.ts +44 -7
  121. package/src/server/__tests__/permissions.test.ts +10 -5
  122. package/src/server/__tests__/prompt-defaults.test.ts +3 -3
  123. package/src/server/admin-ai-alerts.ts +2 -1
  124. package/src/server/admin-ai-usage.ts +2 -1
  125. package/src/server/ai-memory/store.ts +6 -2
  126. package/src/server/{serverFns/ai-prompts → ai-prompts}/prompt-defaults.ts +5 -3
  127. package/src/server/{serverFns/dependency → dependency}/helpers.ts +4 -3
  128. package/src/server/knowledge-public/github-sync.ts +2 -1
  129. package/src/server/license-issue.ts +2 -1
  130. package/src/server/middlewares/auth-rate-limit.ts +117 -0
  131. package/src/server/observability/health-check.ts +4 -1
  132. package/src/server/observability/metrics.ts +17 -15
  133. package/src/server/observability/request-middleware.ts +2 -1
  134. package/src/server/observability/timing.ts +14 -16
  135. package/src/server/serverFns/admin-billing.ts +107 -12
  136. package/src/server/serverFns/ai-memory/list-users.ts +2 -1
  137. package/src/server/serverFns/ai-prompts/activate.ts +2 -1
  138. package/src/server/serverFns/ai-prompts/get.ts +3 -2
  139. package/src/server/serverFns/ai-prompts/list.ts +3 -2
  140. package/src/server/serverFns/ai-prompts/rollback.ts +3 -1
  141. package/src/server/serverFns/ai-prompts/save.ts +5 -3
  142. package/src/server/serverFns/ai-provider/create.ts +4 -1
  143. package/src/server/serverFns/ai-provider/delete.ts +4 -1
  144. package/src/server/serverFns/ai-provider/get.ts +4 -1
  145. package/src/server/serverFns/ai-provider/list.ts +28 -1
  146. package/src/server/serverFns/ai-provider/seed-from-seed.ts +2 -1
  147. package/src/server/serverFns/ai-provider/update.ts +4 -1
  148. package/src/server/serverFns/anyhub/market.ts +8 -1
  149. package/src/server/serverFns/audit.ts +14 -1
  150. package/src/server/serverFns/auth/setup.ts +3 -1
  151. package/src/server/serverFns/compliance.ts +32 -3
  152. package/src/server/serverFns/components/import.ts +69 -14
  153. package/src/server/serverFns/components/list.ts +4 -1
  154. package/src/server/serverFns/components/mutations.ts +9 -1
  155. package/src/server/serverFns/dependency/list.ts +28 -5
  156. package/src/server/serverFns/dependency/sync.ts +7 -2
  157. package/src/server/serverFns/dependency/update.ts +4 -1
  158. package/src/server/serverFns/developer-log-auto-generate.ts +63 -32
  159. package/src/server/serverFns/developer-logs.ts +5 -1
  160. package/src/server/serverFns/invitations.ts +7 -1
  161. package/src/server/serverFns/knowledge-public/bookmarks/create.ts +2 -1
  162. package/src/server/serverFns/knowledge-public/bookmarks/delete.ts +2 -1
  163. package/src/server/serverFns/knowledge-public/bookmarks/detail.ts +2 -1
  164. package/src/server/serverFns/knowledge-public/bookmarks/list.ts +2 -1
  165. package/src/server/serverFns/knowledge-public/bookmarks/sync-all-github.ts +2 -1
  166. package/src/server/serverFns/knowledge-public/bookmarks/update.ts +2 -1
  167. package/src/server/serverFns/knowledge-public/compliance/create.ts +2 -1
  168. package/src/server/serverFns/knowledge-public/compliance/delete.ts +2 -1
  169. package/src/server/serverFns/knowledge-public/compliance/detail.ts +2 -1
  170. package/src/server/serverFns/knowledge-public/compliance/list.ts +2 -1
  171. package/src/server/serverFns/knowledge-public/compliance/update.ts +2 -1
  172. package/src/server/serverFns/knowledge-public/legal/create.ts +2 -1
  173. package/src/server/serverFns/knowledge-public/legal/delete.ts +2 -1
  174. package/src/server/serverFns/knowledge-public/legal/detail.ts +2 -1
  175. package/src/server/serverFns/knowledge-public/legal/list.ts +2 -1
  176. package/src/server/serverFns/knowledge-public/legal/update.ts +2 -1
  177. package/src/server/serverFns/licenses.ts +8 -2
  178. package/src/server/serverFns/monitoring/get-errors.ts +2 -1
  179. package/src/server/serverFns/monitoring/get-health.ts +2 -1
  180. package/src/server/serverFns/monitoring/get-metrics.ts +2 -1
  181. package/src/server/serverFns/monitoring/get-request-logs.ts +2 -1
  182. package/src/server/serverFns/monitoring/report-error.ts +2 -1
  183. package/src/server/serverFns/monitoring/reset.ts +2 -1
  184. package/src/server/serverFns/oauth-providers.ts +4 -1
  185. package/src/server/serverFns/permissions.ts +18 -11
  186. package/src/server/serverFns/sentry/config.ts +11 -8
  187. package/src/server/serverFns/sso-config.ts +17 -2
  188. package/src/server/serverFns/user/create.ts +6 -1
  189. package/src/server/serverFns/user/delete.ts +6 -1
  190. package/src/server/serverFns/user/detail.ts +5 -2
  191. package/src/server/serverFns/user/list.ts +19 -4
  192. package/src/server/serverFns/user/reset-password.ts +10 -2
  193. package/src/server/serverFns/user/update-role.ts +10 -2
  194. package/src/server/serverFns/user/update-status.ts +10 -2
  195. package/src/styles/globals.css +4 -0
  196. package/src/components/ui/input.tsx +0 -19
  197. /package/{src → allblue-ui/src}/hooks/use-mobile.ts +0 -0
  198. /package/{src → allblue-ui/src}/lib/avatar.ts +0 -0
  199. /package/{src/components → allblue-ui/src}/ui/alert.tsx +0 -0
  200. /package/{src/components → allblue-ui/src}/ui/avatar.tsx +0 -0
  201. /package/{src/components → allblue-ui/src}/ui/badge.tsx +0 -0
  202. /package/{src/components → allblue-ui/src}/ui/button.tsx +0 -0
  203. /package/{src/components → allblue-ui/src}/ui/card.tsx +0 -0
  204. /package/{src/components → allblue-ui/src}/ui/collapsible.tsx +0 -0
  205. /package/{src/components → allblue-ui/src}/ui/label.tsx +0 -0
  206. /package/{src/components → allblue-ui/src}/ui/skeleton.tsx +0 -0
  207. /package/{src/components → allblue-ui/src}/ui/textarea.tsx +0 -0
  208. /package/src/routes/{api.auth.$.ts → api/auth/$.ts} +0 -0
  209. /package/src/routes/{api.sentry-test.ts → api/sentry-test.ts} +0 -0
@@ -37,21 +37,12 @@ import {
37
37
  const categoryIconMap: Record<string, LucideIcon> = {
38
38
  Folder, Globe, Code, Book, BookOpen,
39
39
  }
40
- import { DataTable, type AdminColumnDef } from '@/components/data-table/data-table'
41
- import { Button } from '@/components/ui/button'
42
- import { Input } from '@/components/ui/input'
43
- import { Badge } from '@/components/ui/badge'
44
- import {
45
- AlertDialog,
46
- AlertDialogAction,
47
- AlertDialogCancel,
48
- AlertDialogContent,
49
- AlertDialogDescription,
50
- AlertDialogFooter,
51
- AlertDialogHeader,
52
- AlertDialogTitle,
53
- } from '@/components/ui/alert-dialog'
54
- import { toast } from '@/components/ui/toast'
40
+ import { DataTable, type AdminColumnDef } from '@allbluecn/ui/data-table'
41
+ import { Button } from '@allbluecn/ui/button'
42
+ import { Input } from '@allbluecn/ui/input'
43
+ import { Badge } from '@allbluecn/ui/badge'
44
+ import { ConfirmDialog } from '@allbluecn/ui/components/confirm-dialog'
45
+ import { toast } from '@allbluecn/ui/toast'
55
46
  import {
56
47
  BookmarkFormDialog,
57
48
  type BookmarkRow,
@@ -326,29 +317,18 @@ function BookmarksPage() {
326
317
  onSuccess={refresh}
327
318
  />
328
319
 
329
- <AlertDialog open={!!deleting} onOpenChange={(o: boolean) => !o && setDeleting(null)}>
330
- <AlertDialogContent>
331
- <AlertDialogHeader>
332
- <AlertDialogTitle>{m.common_knowledgePublic_bookmarks_deleteConfirmTitle()}</AlertDialogTitle>
333
- <AlertDialogDescription>
334
- {m.common_knowledgePublic_bookmarks_deleteConfirmDesc({ title: deleting?.title ?? '' })}
335
- </AlertDialogDescription>
336
- </AlertDialogHeader>
337
- <AlertDialogFooter>
338
- <AlertDialogCancel disabled={deletePending}>{m.common_common_cancel()}</AlertDialogCancel>
339
- <AlertDialogAction
340
- onClick={(e: React.MouseEvent) => {
341
- e.preventDefault()
342
- confirmDelete()
343
- }}
344
- disabled={deletePending}
345
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
346
- >
347
- {deletePending ? m.common_common_deleting() : m.common_common_delete()}
348
- </AlertDialogAction>
349
- </AlertDialogFooter>
350
- </AlertDialogContent>
351
- </AlertDialog>
320
+ <ConfirmDialog
321
+ open={!!deleting}
322
+ onOpenChange={(o: boolean) => !o && setDeleting(null)}
323
+ title={m.common_knowledgePublic_bookmarks_deleteConfirmTitle()}
324
+ description={m.common_knowledgePublic_bookmarks_deleteConfirmDesc({ title: deleting?.title ?? '' })}
325
+ cancelLabel={m.common_common_cancel()}
326
+ confirmLabel={deletePending ? m.common_common_deleting() : m.common_common_delete()}
327
+ confirmClassName="bg-destructive text-destructive-foreground hover:bg-destructive/90"
328
+ confirmDisabled={deletePending}
329
+ cancelDisabled={deletePending}
330
+ onConfirm={confirmDelete}
331
+ />
352
332
  </div>
353
333
  )
354
334
  }
@@ -19,21 +19,12 @@ import { createLazyFileRoute, useNavigate, useRouter } from '@tanstack/react-rou
19
19
  import { m } from '@/paraglide/messages.js'
20
20
  import { useState, useTransition } from 'react'
21
21
  import { PlusIcon, PencilIcon, Trash2Icon, SearchIcon } from 'lucide-react'
22
- import { DataTable, type AdminColumnDef } from '@/components/data-table/data-table'
23
- import { Button } from '@/components/ui/button'
24
- import { Input } from '@/components/ui/input'
25
- import { Badge } from '@/components/ui/badge'
26
- import {
27
- AlertDialog,
28
- AlertDialogAction,
29
- AlertDialogCancel,
30
- AlertDialogContent,
31
- AlertDialogDescription,
32
- AlertDialogFooter,
33
- AlertDialogHeader,
34
- AlertDialogTitle,
35
- } from '@/components/ui/alert-dialog'
36
- import { toast } from '@/components/ui/toast'
22
+ import { DataTable, type AdminColumnDef } from '@allbluecn/ui/data-table'
23
+ import { Button } from '@allbluecn/ui/button'
24
+ import { Input } from '@allbluecn/ui/input'
25
+ import { Badge } from '@allbluecn/ui/badge'
26
+ import { ConfirmDialog } from '@allbluecn/ui/components/confirm-dialog'
27
+ import { toast } from '@allbluecn/ui/toast'
37
28
  import {
38
29
  ComplianceFormDialog,
39
30
  type ComplianceRow,
@@ -216,29 +207,18 @@ function CompliancePage() {
216
207
  onSuccess={refresh}
217
208
  />
218
209
 
219
- <AlertDialog open={!!deleting} onOpenChange={(o: boolean) => !o && setDeleting(null)}>
220
- <AlertDialogContent>
221
- <AlertDialogHeader>
222
- <AlertDialogTitle>{m.common_knowledgePublic_compliance_deleteConfirmTitle()}</AlertDialogTitle>
223
- <AlertDialogDescription>
224
- {m.common_knowledgePublic_compliance_deleteConfirmDesc({ title: deleting?.title ?? '' })}
225
- </AlertDialogDescription>
226
- </AlertDialogHeader>
227
- <AlertDialogFooter>
228
- <AlertDialogCancel disabled={deletePending}>{m.common_common_cancel()}</AlertDialogCancel>
229
- <AlertDialogAction
230
- onClick={(e: React.MouseEvent) => {
231
- e.preventDefault()
232
- confirmDelete()
233
- }}
234
- disabled={deletePending}
235
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
236
- >
237
- {deletePending ? m.common_common_deleting() : m.common_common_delete()}
238
- </AlertDialogAction>
239
- </AlertDialogFooter>
240
- </AlertDialogContent>
241
- </AlertDialog>
210
+ <ConfirmDialog
211
+ open={!!deleting}
212
+ onOpenChange={(o: boolean) => !o && setDeleting(null)}
213
+ title={m.common_knowledgePublic_compliance_deleteConfirmTitle()}
214
+ description={m.common_knowledgePublic_compliance_deleteConfirmDesc({ title: deleting?.title ?? '' })}
215
+ cancelLabel={m.common_common_cancel()}
216
+ confirmLabel={deletePending ? m.common_common_deleting() : m.common_common_delete()}
217
+ confirmClassName="bg-destructive text-destructive-foreground hover:bg-destructive/90"
218
+ confirmDisabled={deletePending}
219
+ cancelDisabled={deletePending}
220
+ onConfirm={confirmDelete}
221
+ />
242
222
  </div>
243
223
  )
244
224
  }
@@ -19,10 +19,10 @@ import { createLazyFileRoute, useNavigate, useRouter } from '@tanstack/react-rou
19
19
  import { m } from '@/paraglide/messages.js'
20
20
  import { useState, useTransition } from 'react'
21
21
  import { PlusIcon, PencilIcon, Trash2Icon, SearchIcon } from 'lucide-react'
22
- import { DataTable, type AdminColumnDef } from '@/components/data-table/data-table'
23
- import { Button } from '@/components/ui/button'
24
- import { Input } from '@/components/ui/input'
25
- import { Badge } from '@/components/ui/badge'
22
+ import { DataTable, type AdminColumnDef } from '@allbluecn/ui/data-table'
23
+ import { Button } from '@allbluecn/ui/button'
24
+ import { Input } from '@allbluecn/ui/input'
25
+ import { Badge } from '@allbluecn/ui/badge'
26
26
  import {
27
27
  AlertDialog,
28
28
  AlertDialogAction,
@@ -32,8 +32,8 @@ import {
32
32
  AlertDialogFooter,
33
33
  AlertDialogHeader,
34
34
  AlertDialogTitle,
35
- } from '@/components/ui/alert-dialog'
36
- import { toast } from '@/components/ui/toast'
35
+ } from '@allbluecn/ui/alert-dialog'
36
+ import { toast } from '@allbluecn/ui/toast'
37
37
  import { LegalFormDialog, type LegalRow } from '@/components/knowledge-public/legal-form-dialog'
38
38
  import { adminDeleteLegalFn } from '@/server/serverFns/knowledge-public/legal/delete'
39
39
 
@@ -18,10 +18,10 @@
18
18
  import { createLazyFileRoute, Link, useNavigate } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
20
  import { useServerFn } from '@tanstack/react-start'
21
- import { toast } from '@/components/ui/toast'
21
+ import { toast } from '@allbluecn/ui/toast'
22
22
  import type React from 'react'
23
- import { Button } from '@/components/ui/button'
24
- import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
23
+ import { Button } from '@allbluecn/ui/button'
24
+ import { Card, CardContent, CardHeader, CardTitle } from '@allbluecn/ui/card'
25
25
  import { revokeLicenseFn } from '@/server/serverFns/licenses'
26
26
 
27
27
  export const Route = createLazyFileRoute('/_nav/licenses/$licenseId')({
@@ -22,11 +22,11 @@ import { useState } from 'react'
22
22
  import { useForm } from '@tanstack/react-form'
23
23
  import { useServerFn } from '@tanstack/react-start'
24
24
  import { Check, Copy } from 'lucide-react'
25
- import { Button } from '@/components/ui/button'
26
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
27
- import { Input } from '@/components/ui/input'
28
- import { Label } from '@/components/ui/label'
29
- import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
25
+ import { Button } from '@allbluecn/ui/button'
26
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@allbluecn/ui/card'
27
+ import { Input } from '@allbluecn/ui/input'
28
+ import { Label } from '@allbluecn/ui/label'
29
+ import { Alert, AlertDescription, AlertTitle } from '@allbluecn/ui/alert'
30
30
  import { issueLicenseFn } from '@/server/serverFns/licenses'
31
31
  import { FeatureRegistry } from '@allbluecn/kernel'
32
32
 
@@ -18,7 +18,7 @@
18
18
  import { createLazyFileRoute, Link } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
20
  import type React from 'react'
21
- import { Button } from '@/components/ui/button'
21
+ import { Button } from '@allbluecn/ui/button'
22
22
 
23
23
  export const Route = createLazyFileRoute('/_nav/licenses')({
24
24
  pendingComponent: () => (
@@ -8,8 +8,8 @@ import { tooltip } from '@tanstack/charts/tooltip'
8
8
  import { Chart } from '@tanstack/charts/react'
9
9
  import { BarChart3, AlertCircle, AlertTriangle, ChevronLeft, ChevronRight, RefreshCw } from 'lucide-react'
10
10
  import { PageHeader } from '@/components/common/page-header'
11
- import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
12
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
11
+ import { Card, CardContent, CardHeader, CardTitle } from '@allbluecn/ui/card'
12
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@allbluecn/ui/table'
13
13
  import { getAiUsageOverviewFn } from '@/server/serverFns/monitoring/get-ai-usage-overview'
14
14
  import { checkAiAlertsFn } from '@/server/serverFns/monitoring/check-ai-alerts'
15
15
  import type { AiUsageOverview } from '@/server/admin-ai-usage'
@@ -17,37 +17,37 @@
17
17
 
18
18
  import { createLazyFileRoute } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { useState, useEffect } from 'react'
20
+ import { useState } from 'react'
21
21
  import { AlertTriangle, RefreshCw, Trash2 } from 'lucide-react'
22
22
  import { PageHeader } from '@/components/common/page-header'
23
23
  import { ErrorList } from '@/components/monitoring/error-list'
24
+ import { ErrorItem } from '@/components/monitoring/error-list'
24
25
  import { getErrors } from '@/server/serverFns/monitoring/get-errors'
25
26
  import { resetMonitoring } from '@/server/serverFns/monitoring/reset'
26
- import { toast } from '@/components/ui/toast'
27
+ import { jsonify } from '@/lib/loader-serial'
28
+ import { toast } from '@allbluecn/ui/toast'
27
29
 
28
30
  export const Route = createLazyFileRoute('/_nav/monitoring/errors')({
29
31
  component: MonitoringErrors,
30
32
  })
31
33
 
32
34
  function MonitoringErrors() {
33
- const [loading, setLoading] = useState(true)
34
- const [data, setData] = useState<any>(null)
35
+ const initial = Route.useLoaderData()
36
+ const [loading, setLoading] = useState(false)
37
+ const [data, setData] = useState(initial)
35
38
 
36
39
  const loadData = async () => {
37
40
  setLoading(true)
38
41
  try {
39
42
  const result = await getErrors()
40
- setData(result)
43
+ // Date → ISO 字符串,与 loader 预取形态对齐
44
+ setData(jsonify(result))
41
45
  } catch {
42
46
  // ignore
43
47
  }
44
48
  setLoading(false)
45
49
  }
46
50
 
47
- useEffect(() => {
48
- loadData()
49
- }, [])
50
-
51
51
  const handleReset = async () => {
52
52
  try {
53
53
  await resetMonitoring({ data: { type: 'errors' } })
@@ -101,7 +101,7 @@ function MonitoringErrors() {
101
101
  <RefreshCw className="size-5 animate-spin text-muted-foreground" />
102
102
  </div>
103
103
  ) : data?.errors?.length > 0 ? (
104
- <ErrorList errors={data.errors} />
104
+ <ErrorList errors={data.errors as ErrorItem[]} />
105
105
  ) : (
106
106
  <div className="flex items-center justify-center h-32 text-sm text-muted-foreground">{m.common_monitoring_errors_empty()}</div>
107
107
  )}
@@ -0,0 +1,7 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getErrors } from '@/server/serverFns/monitoring/get-errors'
3
+ import { jsonify } from '@/lib/loader-serial'
4
+
5
+ export const Route = createFileRoute('/_nav/monitoring/errors')({
6
+ loader: async () => jsonify(await getErrors()),
7
+ })
@@ -18,32 +18,29 @@
18
18
  import { createLazyFileRoute } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
20
  import { useState, useEffect, useRef } from 'react'
21
- import { formatTimestamp } from '@allbluecn/web-app/lib/datetime'
21
+ import { formatTimestamp } from '@allbluecn/ui/lib/datetime'
22
22
  import { HeartPulse, Play, Square, RefreshCw } from 'lucide-react'
23
23
  import { PageHeader } from '@/components/common/page-header'
24
24
  import { HealthCards } from '@/components/monitoring/health-cards'
25
- import { DataTable } from '@/components/data-table/data-table'
25
+ import { DataTable } from '@allbluecn/ui/data-table'
26
26
  import { getHealthStatus } from '@/server/serverFns/monitoring/get-health'
27
27
  import { getHealthCheckSettings, updateHealthCheckSettings } from '@/server/serverFns/monitoring/get-health-settings'
28
- import { toast } from '@/components/ui/toast'
28
+ import { toast } from '@allbluecn/ui/toast'
29
+ import { jsonify } from '@/lib/loader-serial'
29
30
 
30
31
  export const Route = createLazyFileRoute('/_nav/monitoring/health')({
31
32
  component: MonitoringHealth,
32
33
  })
33
34
 
34
35
  function MonitoringHealth() {
35
- const [loading, setLoading] = useState(true)
36
- const [data, setData] = useState<any>(null)
37
- const [autoEnabled, setAutoEnabled] = useState(true)
38
- const [autoLoading, setAutoLoading] = useState(true)
36
+ const initial = Route.useLoaderData()
37
+ const [loading, setLoading] = useState(false)
38
+ const [data, setData] = useState(initial.data)
39
+ const [autoEnabled, setAutoEnabled] = useState(initial.settings.enabled)
40
+ const [autoLoading, setAutoLoading] = useState(false)
39
41
  const [countdown, setCountdown] = useState(30)
40
42
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
41
43
 
42
- useEffect(() => {
43
- loadHealth()
44
- loadSettings()
45
- }, [])
46
-
47
44
  useEffect(() => {
48
45
  if (timerRef.current) clearInterval(timerRef.current)
49
46
  if (autoEnabled) {
@@ -64,7 +61,8 @@ function MonitoringHealth() {
64
61
  setLoading(true)
65
62
  try {
66
63
  const result = await getHealthStatus()
67
- setData(result)
64
+ // Date → ISO 字符串,与 loader 预取形态对齐
65
+ setData(jsonify(result))
68
66
  } catch {
69
67
  // ignore
70
68
  }
@@ -158,7 +156,7 @@ function MonitoringHealth() {
158
156
  <p className="text-sm font-medium">{m.common_monitoring_health_database_title()}</p>
159
157
  <p className="text-xs text-muted-foreground mt-1">
160
158
  {data.current.checks.database.status === 'connected'
161
- ? m.common_monitoring_health_database_connected({ latency: data.current.checks.database.latency })
159
+ ? m.common_monitoring_health_database_connected({ latency: data.current.checks.database.latency! })
162
160
  : `${m.common_monitoring_health_database_failed()}: ${data.current.checks.database.error || m.common_monitoring_health_database_unknownError()}`}
163
161
  </p>
164
162
  </div>
@@ -178,7 +176,7 @@ function MonitoringHealth() {
178
176
  <h3 className="text-sm font-medium">{m.common_monitoring_health_history()}</h3>
179
177
  </div>
180
178
  <div className="p-4 flex-1 min-h-0">
181
- <HistoryTable data={data.recent} />
179
+ <HistoryTable data={data.recent as HealthHistoryRow[]} />
182
180
  </div>
183
181
  </div>
184
182
  )}
@@ -0,0 +1,14 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getHealthStatus } from '@/server/serverFns/monitoring/get-health'
3
+ import { getHealthCheckSettings } from '@/server/serverFns/monitoring/get-health-settings'
4
+ import { jsonify } from '@/lib/loader-serial'
5
+
6
+ export const Route = createFileRoute('/_nav/monitoring/health')({
7
+ loader: async () => {
8
+ const [data, settings] = await Promise.all([
9
+ getHealthStatus(),
10
+ getHealthCheckSettings(),
11
+ ])
12
+ return jsonify({ data, settings })
13
+ },
14
+ })
@@ -17,24 +17,28 @@
17
17
 
18
18
  import { createLazyFileRoute } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { useState, useEffect } from 'react'
20
+ import { useState, useEffect, useRef } from 'react'
21
21
  import { Activity, Filter, RefreshCw, Trash2 } from 'lucide-react'
22
22
  import { PageHeader } from '@/components/common/page-header'
23
23
  import { RequestLogTable } from '@/components/monitoring/request-log-table'
24
- import { Checkbox } from '@/components/ui/checkbox'
24
+ import { Checkbox } from '@allbluecn/ui/checkbox'
25
25
  import { getRequestLogs } from '@/server/serverFns/monitoring/get-request-logs'
26
26
  import { resetMonitoring } from '@/server/serverFns/monitoring/reset'
27
- import { DataTablePagination } from '@/components/data-table/data-table-pagination'
28
- import { toast } from '@/components/ui/toast'
27
+ import { DataTablePagination } from '@allbluecn/ui/data-table/pagination'
28
+ import { toast } from '@allbluecn/ui/toast'
29
+ import { jsonify } from '@/lib/loader-serial'
30
+ import { RequestLogEntry } from '@/components/monitoring/request-log-table'
29
31
 
30
32
  export const Route = createLazyFileRoute('/_nav/monitoring/logs')({
31
33
  component: MonitoringLogs,
32
34
  })
33
35
 
34
36
  function MonitoringLogs() {
35
- const [loading, setLoading] = useState(true)
36
- const [logs, setLogs] = useState<any[]>([])
37
- const [total, setTotal] = useState(0)
37
+ const initial = Route.useLoaderData()
38
+ const skipFirstLoadRef = useRef(true)
39
+ const [loading, setLoading] = useState(false)
40
+ const [logs, setLogs] = useState(initial.logs)
41
+ const [total, setTotal] = useState(initial.total)
38
42
  const [page, setPage] = useState(1)
39
43
  const [method, setMethod] = useState('')
40
44
  const [status, setStatus] = useState('')
@@ -55,7 +59,8 @@ function MonitoringLogs() {
55
59
  excludeMonitoring: filterMonitoring,
56
60
  },
57
61
  })
58
- setLogs(result.logs)
62
+ // Date → ISO 字符串,与 loader 预取形态对齐
63
+ setLogs(jsonify(result.logs))
59
64
  setTotal(result.total)
60
65
  } catch {
61
66
  // ignore
@@ -64,6 +69,11 @@ function MonitoringLogs() {
64
69
  }
65
70
 
66
71
  useEffect(() => {
72
+ // 初始数据已由 route.tsx loader 预取(与 loader 同参),跳过挂载即重复请求
73
+ if (skipFirstLoadRef.current) {
74
+ skipFirstLoadRef.current = false
75
+ return
76
+ }
67
77
  loadLogs()
68
78
  }, [page, method, status, path, filterMonitoring])
69
79
 
@@ -145,13 +155,18 @@ function MonitoringLogs() {
145
155
  ) : logs.length > 0 ? (
146
156
  <div className="flex-1 min-h-0 flex flex-col">
147
157
  <RequestLogTable
148
- logs={logs}
158
+ logs={logs as RequestLogEntry[]}
149
159
  actions={
150
160
  <DataTablePagination
151
161
  page={page}
152
162
  totalPages={Math.ceil(total / limit)}
153
163
  total={total}
154
164
  onPageChange={setPage}
165
+ labels={{
166
+ total: m.common_pagination_total({ total, page, totalPages: Math.ceil(total / limit) }),
167
+ previous: m.common_pagination_prev(),
168
+ next: m.common_pagination_next(),
169
+ }}
155
170
  />
156
171
  }
157
172
  />
@@ -0,0 +1,13 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getRequestLogs } from '@/server/serverFns/monitoring/get-request-logs'
3
+ import { jsonify } from '@/lib/loader-serial'
4
+
5
+ export const Route = createFileRoute('/_nav/monitoring/logs')({
6
+ loader: async () => {
7
+ const result = await getRequestLogs({
8
+ data: { limit: 50, offset: 0, excludeMonitoring: false },
9
+ })
10
+ // Date → ISO 字符串:与客户端 RPC 返回形态对齐(组件按 string 时间戳渲染)
11
+ return jsonify(result)
12
+ },
13
+ })
@@ -15,20 +15,20 @@
15
15
  // You should have received a copy of the GNU Affero General Public License
16
16
  // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
- import { describeMetric } from '@/lib/monitoring-descriptions'
19
18
  import { m } from '@/paraglide/messages.js'
20
19
  import { getBenchmark, getBenchmarkSource, compareDuration } from '@/lib/monitoring-benchmarks'
21
- import { formatTimestamp } from '@allbluecn/web-app/lib/datetime'
20
+ import { formatTimestamp } from '@allbluecn/ui/lib/datetime'
22
21
  import { createLazyFileRoute } from '@tanstack/react-router'
23
- import { useState, useEffect } from 'react'
22
+ import { useState } from 'react'
24
23
  import { BarChart3, RefreshCw, Trash2, MemoryStick } from 'lucide-react'
25
24
  import { ArrowDown, ArrowUp } from 'lucide-react'
26
25
  import { PageHeader } from '@/components/common/page-header'
27
26
  import { MetricsStats } from '@/components/monitoring/metrics-stats'
28
27
  import { getMetrics } from '@/server/serverFns/monitoring/get-metrics'
29
28
  import { resetMonitoring } from '@/server/serverFns/monitoring/reset'
30
- import { DataTable } from '@/components/data-table/data-table'
31
- import { toast } from '@/components/ui/toast'
29
+ import { jsonify } from '@/lib/loader-serial'
30
+ import { DataTable } from '@allbluecn/ui/data-table'
31
+ import { toast } from '@allbluecn/ui/toast'
32
32
 
33
33
  export const Route = createLazyFileRoute('/_nav/monitoring/metrics')({
34
34
  component: MonitoringMetrics,
@@ -41,24 +41,22 @@ function formatBytes(bytes: number): string {
41
41
  }
42
42
 
43
43
  function MonitoringMetrics() {
44
- const [loading, setLoading] = useState(true)
45
- const [data, setData] = useState<any>(null)
44
+ const initial = Route.useLoaderData()
45
+ const [loading, setLoading] = useState(false)
46
+ const [data, setData] = useState(initial)
46
47
 
47
48
  const loadData = async () => {
48
49
  setLoading(true)
49
50
  try {
50
51
  const result = await getMetrics()
51
- setData(result)
52
+ // Date → ISO 字符串,与 loader 预取形态对齐
53
+ setData(jsonify(result))
52
54
  } catch {
53
55
  // ignore
54
56
  }
55
57
  setLoading(false)
56
58
  }
57
59
 
58
- useEffect(() => {
59
- loadData()
60
- }, [])
61
-
62
60
  const handleReset = async () => {
63
61
  try {
64
62
  await resetMonitoring({ data: { type: 'metrics' } })
@@ -133,7 +131,7 @@ function MonitoringMetrics() {
133
131
  <h3 className="text-sm font-medium">{m.common_monitoring_metrics_history()}</h3>
134
132
  </div>
135
133
  <div className="p-4 flex-1 min-h-0">
136
- <MetricsHistoryTable data={data.history} />
134
+ <MetricsHistoryTable data={data.history as MetricsHistoryRow[]} />
137
135
  </div>
138
136
  </div>
139
137
  )}
@@ -141,7 +139,7 @@ function MonitoringMetrics() {
141
139
  )
142
140
  }
143
141
 
144
- interface MetricsHistoryRow {
142
+ export interface MetricsHistoryRow {
145
143
  metricName: string
146
144
  count: number
147
145
  avg: number
@@ -0,0 +1,7 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getMetrics } from '@/server/serverFns/monitoring/get-metrics'
3
+ import { jsonify } from '@/lib/loader-serial'
4
+
5
+ export const Route = createFileRoute('/_nav/monitoring/metrics')({
6
+ loader: async () => jsonify(await getMetrics()),
7
+ })
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { createLazyFileRoute } from '@tanstack/react-router'
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { useState, useEffect } from 'react'
20
+ import { useState } from 'react'
21
21
  import { Activity, RefreshCw } from 'lucide-react'
22
22
  import { PageHeader } from '@/components/common/page-header'
23
23
  import { HealthCards } from '@/components/monitoring/health-cards'
@@ -26,16 +26,19 @@ import { MetricsStats } from '@/components/monitoring/metrics-stats'
26
26
  import { getHealthStatus } from '@/server/serverFns/monitoring/get-health'
27
27
  import { getErrors } from '@/server/serverFns/monitoring/get-errors'
28
28
  import { getMetrics } from '@/server/serverFns/monitoring/get-metrics'
29
+ import { jsonify } from '@/lib/loader-serial'
30
+ import { ErrorItem } from '@/components/monitoring/error-list'
29
31
 
30
32
  export const Route = createLazyFileRoute('/_nav/monitoring/overview')({
31
33
  component: MonitoringOverview,
32
34
  })
33
35
 
34
36
  function MonitoringOverview() {
35
- const [loading, setLoading] = useState(true)
36
- const [health, setHealth] = useState<any>(null)
37
- const [errors, setErrors] = useState<any>(null)
38
- const [metrics, setMetrics] = useState<any>(null)
37
+ const initial = Route.useLoaderData()
38
+ const [loading, setLoading] = useState(false)
39
+ const [health, setHealth] = useState(initial.health)
40
+ const [errors, setErrors] = useState(initial.errors)
41
+ const [metrics, setMetrics] = useState(initial.metrics)
39
42
 
40
43
  const loadData = async () => {
41
44
  setLoading(true)
@@ -45,19 +48,17 @@ function MonitoringOverview() {
45
48
  getErrors(),
46
49
  getMetrics(),
47
50
  ])
48
- setHealth(h)
49
- setErrors(e)
50
- setMetrics(m)
51
+ // Date → ISO 字符串,与 loader 预取形态对齐
52
+ const j = jsonify({ h, e, m })
53
+ setHealth(j.h)
54
+ setErrors(j.e)
55
+ setMetrics(j.m)
51
56
  } catch {
52
57
  // ignore
53
58
  }
54
59
  setLoading(false)
55
60
  }
56
61
 
57
- useEffect(() => {
58
- loadData()
59
- }, [])
60
-
61
62
  if (loading) {
62
63
  return (
63
64
  <div className="flex items-center justify-center h-64">
@@ -112,7 +113,7 @@ function MonitoringOverview() {
112
113
  </div>
113
114
  <div className="p-4">
114
115
  {errors?.errors?.slice(0, 5).length > 0 ? (
115
- <ErrorList errors={errors.errors.slice(0, 5)} />
116
+ <ErrorList errors={errors.errors.slice(0, 5) as ErrorItem[]} />
116
117
  ) : (
117
118
  <p className="text-sm text-muted-foreground text-center py-4">{m.common_monitoring_overview_recentErrors_empty()}</p>
118
119
  )}
@@ -0,0 +1,17 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { getHealthStatus } from '@/server/serverFns/monitoring/get-health'
3
+ import { getErrors } from '@/server/serverFns/monitoring/get-errors'
4
+ import { getMetrics } from '@/server/serverFns/monitoring/get-metrics'
5
+ import { jsonify } from '@/lib/loader-serial'
6
+
7
+ export const Route = createFileRoute('/_nav/monitoring/overview')({
8
+ loader: async () => {
9
+ const [health, errors, metrics] = await Promise.all([
10
+ getHealthStatus(),
11
+ getErrors(),
12
+ getMetrics(),
13
+ ])
14
+ // Date → ISO 字符串:与客户端 RPC 返回形态对齐(组件按 string 时间戳渲染)
15
+ return jsonify({ health, errors, metrics })
16
+ },
17
+ })