@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
@@ -22,7 +22,7 @@ import {
22
22
  SidebarHeader,
23
23
  SidebarTrigger,
24
24
  useSidebar,
25
- } from "@/components/ui/sidebar";
25
+ } from "@allbluecn/ui/sidebar";
26
26
  import { cn } from "@/lib/utils";
27
27
  import { m } from '@/paraglide/messages.js'
28
28
  import { motion } from "motion/react";
@@ -40,7 +40,6 @@ import {
40
40
  Scale,
41
41
  ShieldCheck,
42
42
  Bookmark,
43
- BugPlay,
44
43
  Activity,
45
44
  CreditCard,
46
45
  Shield,
@@ -16,7 +16,7 @@
16
16
  // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
18
  import type { ReactNode } from "react"
19
- import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
19
+ import { SidebarInset, SidebarProvider } from "@allbluecn/ui/sidebar";
20
20
  import { DashboardSidebar } from "@/components/layout/sidebar-02/app-sidebar";
21
21
 
22
22
  export default function Sidebar02({ children }: { children?: ReactNode }) {
@@ -19,8 +19,7 @@
19
19
  import {
20
20
  Collapsible,
21
21
  CollapsibleContent,
22
- CollapsibleTrigger,
23
- } from "@/components/ui/collapsible";
22
+ } from "@allbluecn/ui/collapsible";
24
23
  import {
25
24
  SidebarMenu,
26
25
  SidebarMenuButton,
@@ -29,7 +28,7 @@ import {
29
28
  SidebarMenuSubButton,
30
29
  SidebarMenuItem as SidebarMenuSubItem,
31
30
  useSidebar,
32
- } from "@/components/ui/sidebar";
31
+ } from "@allbluecn/ui/sidebar";
33
32
  import { cn } from "@/lib/utils";
34
33
  import { ChevronDown, ChevronUp } from "lucide-react";
35
34
  import { Link } from "@tanstack/react-router";
@@ -16,8 +16,8 @@
16
16
  // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
18
 
19
- import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
20
- import { Button } from "@/components/ui/button";
19
+ import { Avatar, AvatarFallback, AvatarImage } from "@allbluecn/ui/avatar";
20
+ import { Button } from "@allbluecn/ui/button";
21
21
  import {
22
22
  DropdownMenu,
23
23
  DropdownMenuContent,
@@ -25,7 +25,7 @@ import {
25
25
  DropdownMenuLabel,
26
26
  DropdownMenuSeparator,
27
27
  DropdownMenuTrigger,
28
- } from "@/components/ui/dropdown-menu";
28
+ } from "@allbluecn/ui/dropdown-menu";
29
29
  import { BellIcon } from "lucide-react";
30
30
 
31
31
  type Notification = {
@@ -15,17 +15,17 @@
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 type { AdminColumnDef } from "@/components/data-table/data-table";
18
+ import type { AdminColumnDef } from "@allbluecn/ui/data-table";
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { Badge } from "@/components/ui/badge"
21
- import { Button } from "@/components/ui/button"
20
+ import { Badge } from "@allbluecn/ui/badge"
21
+ import { Button } from "@allbluecn/ui/button"
22
22
  import {
23
23
  DropdownMenu,
24
24
  DropdownMenuContent,
25
25
  DropdownMenuItem,
26
26
  DropdownMenuSeparator,
27
27
  DropdownMenuTrigger,
28
- } from "@/components/ui/dropdown-menu"
28
+ } from "@allbluecn/ui/dropdown-menu"
29
29
  import { MoreHorizontalIcon, PencilIcon, TrashIcon, EyeIcon, EyeOffIcon } from "lucide-react"
30
30
 
31
31
  export type MarketAppRow = {
@@ -17,7 +17,6 @@
17
17
 
18
18
  import { useEffect, useState } from "react"
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { useTheme } from "next-themes"
21
20
  import { MarketAppFormData } from "@/lib/anyhub/types"
22
21
  import { CAPABILITY_GROUPS } from "@/lib/anyhub/capabilities"
23
22
  import {
@@ -26,18 +25,18 @@ import {
26
25
  DialogFooter,
27
26
  DialogHeader,
28
27
  DialogTitle,
29
- } from "@/components/ui/dialog"
30
- import { Button } from "@/components/ui/button"
31
- import { Input } from "@/components/ui/input"
32
- import { Label } from "@/components/ui/label"
28
+ } from "@allbluecn/ui/dialog"
29
+ import { Button } from "@allbluecn/ui/button"
30
+ import { Input } from "@allbluecn/ui/input"
31
+ import { Label } from "@allbluecn/ui/label"
33
32
  import {
34
33
  Select,
35
34
  SelectContent,
36
35
  SelectItem,
37
36
  SelectTrigger,
38
37
  SelectValue,
39
- } from "@/components/ui/select"
40
- import { toast } from '@/components/ui/toast'
38
+ } from "@allbluecn/ui/select"
39
+ import { toast } from '@allbluecn/ui/toast'
41
40
 
42
41
  interface MarketAppFormProps {
43
42
  open: boolean
@@ -17,24 +17,15 @@
17
17
 
18
18
  import { useCallback, useEffect, useState } from "react"
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { DataTable } from "@/components/data-table/data-table"
20
+ import { DataTable } from "@allbluecn/ui/data-table"
21
21
  import { getMarketColumns, MarketAppRow } from "./columns"
22
22
  import { MarketAppForm } from "./market-app-form"
23
23
  import { MarketAppFormData } from "@/lib/anyhub/types"
24
- import { Button } from "@/components/ui/button"
24
+ import { Button } from "@allbluecn/ui/button"
25
25
  import { PlusIcon } from "lucide-react"
26
- import { toast } from '@/components/ui/toast'
26
+ import { toast } from '@allbluecn/ui/toast'
27
27
  import { listMarketAppsFn, updateMarketAppFn, deleteMarketAppFn } from "@/server/serverFns/anyhub/market"
28
- import {
29
- AlertDialog,
30
- AlertDialogAction,
31
- AlertDialogCancel,
32
- AlertDialogContent,
33
- AlertDialogDescription,
34
- AlertDialogFooter,
35
- AlertDialogHeader,
36
- AlertDialogTitle,
37
- } from "@/components/ui/alert-dialog"
28
+ import { ConfirmDialog } from "@allbluecn/ui/components/confirm-dialog"
38
29
 
39
30
  export function MarketAppList() {
40
31
  const [apps, setApps] = useState<MarketAppRow[]>([])
@@ -134,22 +125,16 @@ export function MarketAppList() {
134
125
  onSuccess={fetchApps}
135
126
  />
136
127
 
137
- <AlertDialog open={!!deleteTarget} onOpenChange={(open: boolean) => !open && setDeleteTarget(null)}>
138
- <AlertDialogContent>
139
- <AlertDialogHeader>
140
- <AlertDialogTitle>{m.common_market_deleteConfirmTitle()}</AlertDialogTitle>
141
- <AlertDialogDescription>
142
- {m.common_market_deleteConfirmDesc({ name: deleteTarget?.displayName ?? '' })}
143
- </AlertDialogDescription>
144
- </AlertDialogHeader>
145
- <AlertDialogFooter>
146
- <AlertDialogCancel>{m.common_common_cancel()}</AlertDialogCancel>
147
- <AlertDialogAction onClick={handleDelete} className="bg-destructive text-white hover:bg-destructive/90">
148
- {m.common_common_delete()}
149
- </AlertDialogAction>
150
- </AlertDialogFooter>
151
- </AlertDialogContent>
152
- </AlertDialog>
128
+ <ConfirmDialog
129
+ open={!!deleteTarget}
130
+ onOpenChange={(open: boolean) => !open && setDeleteTarget(null)}
131
+ title={m.common_market_deleteConfirmTitle()}
132
+ description={m.common_market_deleteConfirmDesc({ name: deleteTarget?.displayName ?? '' })}
133
+ cancelLabel={m.common_common_cancel()}
134
+ confirmLabel={m.common_common_delete()}
135
+ confirmClassName="bg-destructive text-white hover:bg-destructive/90"
136
+ onConfirm={handleDelete}
137
+ />
153
138
  </div>
154
139
  )
155
140
  }
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { useState } from 'react'
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { DataTable, type AdminColumnDef } from '@/components/data-table/data-table'
20
+ import { DataTable, type AdminColumnDef } from '@allbluecn/ui/data-table'
21
21
 
22
22
  function formatRelativeTime(dateStr: string): string {
23
23
  const now = new Date()
@@ -33,7 +33,7 @@ function formatRelativeTime(dateStr: string): string {
33
33
  return m.common_common_timeAgo_days({ count: days })
34
34
  }
35
35
 
36
- interface ErrorItem {
36
+ export interface ErrorItem {
37
37
  id: string
38
38
  errorName: string
39
39
  errorMessage: string
@@ -15,7 +15,7 @@
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 { DataTable, type AdminColumnDef } from '@/components/data-table/data-table'
18
+ import { DataTable, type AdminColumnDef } from '@allbluecn/ui/data-table'
19
19
  import { m } from '@/paraglide/messages.js'
20
20
  import { describeMetric } from '@/lib/monitoring-descriptions'
21
21
  import { getBenchmark, getBenchmarkSource, compareDuration } from '@/lib/monitoring-benchmarks'
@@ -17,12 +17,12 @@
17
17
 
18
18
  import type React from 'react'
19
19
  import { m } from '@/paraglide/messages.js'
20
- import { DataTable, type AdminColumnDef } from '@/components/data-table/data-table'
20
+ import { DataTable, type AdminColumnDef } from '@allbluecn/ui/data-table'
21
21
  import { describePath } from '@/lib/monitoring-descriptions'
22
22
  import { getBenchmark, getBenchmarkSource, compareDuration } from '@/lib/monitoring-benchmarks'
23
23
  import { ArrowDown, ArrowUp, Info } from 'lucide-react'
24
24
 
25
- interface RequestLogEntry {
25
+ export interface RequestLogEntry {
26
26
  id: string
27
27
  timestamp: string
28
28
  method: string
@@ -19,26 +19,17 @@ import { useEffect, useState } from 'react'
19
19
  import type React from 'react'
20
20
  import { m } from '@/paraglide/messages.js'
21
21
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
22
- import { toast } from '@/components/ui/toast'
22
+ import { toast } from '@allbluecn/ui/toast'
23
23
  import { Eye, EyeOff, KeyRound, PlugZap, Save, Trash2 } from 'lucide-react'
24
24
  import { PageHeader } from '@/components/common/page-header'
25
- import { Button } from '@/components/ui/button'
26
- import { Badge } from '@/components/ui/badge'
27
- import { Card, CardContent } from '@/components/ui/card'
28
- import { Input } from '@/components/ui/input'
29
- import { Label } from '@/components/ui/label'
30
- import { Switch } from '@/components/ui/switch'
31
- import { Skeleton } from '@/components/ui/skeleton'
32
- import {
33
- AlertDialog,
34
- AlertDialogAction,
35
- AlertDialogCancel,
36
- AlertDialogContent,
37
- AlertDialogDescription,
38
- AlertDialogFooter,
39
- AlertDialogHeader,
40
- AlertDialogTitle,
41
- } from '@/components/ui/alert-dialog'
25
+ import { Button } from '@allbluecn/ui/button'
26
+ import { Badge } from '@allbluecn/ui/badge'
27
+ import { Card, CardContent } from '@allbluecn/ui/card'
28
+ import { Input } from '@allbluecn/ui/input'
29
+ import { Label } from '@allbluecn/ui/label'
30
+ import { Switch } from '@allbluecn/ui/switch'
31
+ import { Skeleton } from '@allbluecn/ui/skeleton'
32
+ import { ConfirmDialog } from '@allbluecn/ui/components/confirm-dialog'
42
33
  import {
43
34
  deleteOAuthProviderFn,
44
35
  listOAuthProvidersFn,
@@ -299,26 +290,17 @@ export function OAuthProviderPage(): React.ReactElement {
299
290
  </Card>
300
291
  </div>
301
292
 
302
- <AlertDialog open={pendingDelete} onOpenChange={(o: boolean) => !o && setPendingDelete(false)}>
303
- <AlertDialogContent>
304
- <AlertDialogHeader>
305
- <AlertDialogTitle>{m["admin-loginProviders_delete_confirm_title"]()}</AlertDialogTitle>
306
- <AlertDialogDescription>
307
- {m["admin-loginProviders_delete_confirm_desc"]()}
308
- </AlertDialogDescription>
309
- </AlertDialogHeader>
310
- <AlertDialogFooter>
311
- <AlertDialogCancel>{m.common_common_cancel()}</AlertDialogCancel>
312
- <AlertDialogAction
313
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
314
- disabled={deleteMutation.isPending}
315
- onClick={() => deleteMutation.mutate()}
316
- >
317
- {m["admin-loginProviders_action_delete"]()}
318
- </AlertDialogAction>
319
- </AlertDialogFooter>
320
- </AlertDialogContent>
321
- </AlertDialog>
293
+ <ConfirmDialog
294
+ open={pendingDelete}
295
+ onOpenChange={(o: boolean) => !o && setPendingDelete(false)}
296
+ title={m["admin-loginProviders_delete_confirm_title"]()}
297
+ description={m["admin-loginProviders_delete_confirm_desc"]()}
298
+ cancelLabel={m.common_common_cancel()}
299
+ confirmLabel={m["admin-loginProviders_action_delete"]()}
300
+ confirmClassName="bg-destructive text-destructive-foreground hover:bg-destructive/90"
301
+ confirmDisabled={deleteMutation.isPending}
302
+ onConfirm={() => deleteMutation.mutate()}
303
+ />
322
304
  </div>
323
305
  )
324
306
  }
@@ -0,0 +1,135 @@
1
+ import { useEffect, useState } from 'react'
2
+ import type React from 'react'
3
+ import { m } from '@/paraglide/messages.js'
4
+ import { useServerFn } from '@tanstack/react-start'
5
+ import { toast } from '@allbluecn/ui/toast'
6
+ import {
7
+ assignRoleFn,
8
+ unassignRoleFn,
9
+ listRoleAssignmentsFn,
10
+ } from '@/server/serverFns/permissions'
11
+ import {
12
+ Dialog,
13
+ DialogContent,
14
+ DialogDescription,
15
+ DialogFooter,
16
+ DialogHeader,
17
+ DialogTitle,
18
+ } from '@allbluecn/ui/dialog'
19
+ import { Button } from '@allbluecn/ui/button'
20
+ import { Input } from '@allbluecn/ui/input'
21
+ import {
22
+ Table,
23
+ TableBody,
24
+ TableCell,
25
+ TableHead,
26
+ TableHeader,
27
+ TableRow,
28
+ } from '@allbluecn/ui/table'
29
+ import type { AssignmentItem, RoleItem } from './types'
30
+
31
+ export function AssignDialog({
32
+ open,
33
+ onOpenChange,
34
+ role,
35
+ i18nLang,
36
+ onAssigned,
37
+ }: {
38
+ open: boolean
39
+ onOpenChange: (v: boolean) => void
40
+ role: RoleItem | null
41
+ i18nLang: string
42
+ onAssigned: () => void
43
+ }): React.ReactElement {
44
+ const assignRole = useServerFn(assignRoleFn)
45
+ const unassignRole = useServerFn(unassignRoleFn)
46
+ const listAssignments = useServerFn(listRoleAssignmentsFn)
47
+ const [assignments, setAssignments] = useState<AssignmentItem[]>([])
48
+ const [searchUser, setSearchUser] = useState('')
49
+ const isZh = i18nLang === 'zh'
50
+
51
+ useEffect(() => {
52
+ if (open && role) {
53
+ listAssignments({ data: { roleId: role.id } })
54
+ .then((result: any) => setAssignments(result))
55
+ .catch(() => setAssignments([]))
56
+ }
57
+ }, [open, role, listAssignments])
58
+
59
+ const handleAssign = async () => {
60
+ if (!searchUser.trim() || !role) return
61
+ await assignRole({ data: { userId: searchUser.trim(), roleId: role.id } })
62
+ toast.success(m.enterprise_permissions_toast_assigned())
63
+ setSearchUser('')
64
+ onAssigned()
65
+ }
66
+
67
+ const handleUnassign = async (assignmentId: string) => {
68
+ await unassignRole({ data: { assignmentId } })
69
+ toast.success(m.enterprise_permissions_toast_unassigned())
70
+ setAssignments(assignments.filter((a) => a.id !== assignmentId))
71
+ }
72
+
73
+ return (
74
+ <Dialog open={open} onOpenChange={onOpenChange}>
75
+ <DialogContent className="max-w-lg">
76
+ <DialogHeader>
77
+ <DialogTitle>
78
+ {m.enterprise_permissions_assign({ name: role?.name ?? '' })}
79
+ </DialogTitle>
80
+ <DialogDescription>{m.enterprise_permissions_assignDesc()}</DialogDescription>
81
+ </DialogHeader>
82
+
83
+ <div className="space-y-4">
84
+ <div className="flex gap-2">
85
+ <Input
86
+ value={searchUser}
87
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearchUser(e.target.value)}
88
+ placeholder={m.enterprise_permissions_searchUserPlaceholder()}
89
+ />
90
+ <Button onClick={handleAssign} disabled={!searchUser.trim()}>
91
+ {m.enterprise_permissions_assignUser()}
92
+ </Button>
93
+ </div>
94
+
95
+ {assignments.length === 0 ? (
96
+ <p className="text-sm text-muted-foreground">{m.enterprise_permissions_noAssignments()}</p>
97
+ ) : (
98
+ <Table>
99
+ <TableHeader>
100
+ <TableRow>
101
+ <TableHead>{isZh ? '用户' : 'User'}</TableHead>
102
+ <TableHead>{isZh ? '邮箱' : 'Email'}</TableHead>
103
+ <TableHead>{isZh ? '操作' : 'Actions'}</TableHead>
104
+ </TableRow>
105
+ </TableHeader>
106
+ <TableBody>
107
+ {assignments.map((a) => (
108
+ <TableRow key={a.id}>
109
+ <TableCell className="font-medium">{a.user?.username || a.userId}</TableCell>
110
+ <TableCell>{a.user?.email || '-'}</TableCell>
111
+ <TableCell>
112
+ <button
113
+ type="button"
114
+ className="text-destructive hover:underline"
115
+ onClick={() => handleUnassign(a.id)}
116
+ >
117
+ {m.enterprise_permissions_unassign()}
118
+ </button>
119
+ </TableCell>
120
+ </TableRow>
121
+ ))}
122
+ </TableBody>
123
+ </Table>
124
+ )}
125
+ </div>
126
+
127
+ <DialogFooter>
128
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
129
+ {m.enterprise_permissions_dialog_close()}
130
+ </Button>
131
+ </DialogFooter>
132
+ </DialogContent>
133
+ </Dialog>
134
+ )
135
+ }
@@ -0,0 +1,135 @@
1
+ import { useCallback, useEffect, useState } from 'react'
2
+ import type React from 'react'
3
+ import { m } from '@/paraglide/messages.js'
4
+ import { useServerFn } from '@tanstack/react-start'
5
+ import { toast } from '@allbluecn/ui/toast'
6
+ import { createRoleFn } from '@/server/serverFns/permissions'
7
+ import {
8
+ Dialog,
9
+ DialogContent,
10
+ DialogDescription,
11
+ DialogFooter,
12
+ DialogHeader,
13
+ DialogTitle,
14
+ } from '@allbluecn/ui/dialog'
15
+ import { Button } from '@allbluecn/ui/button'
16
+ import { Input } from '@allbluecn/ui/input'
17
+ import { Label } from '@allbluecn/ui/label'
18
+ import {
19
+ Select,
20
+ SelectContent,
21
+ SelectItem,
22
+ SelectTrigger,
23
+ SelectValue,
24
+ } from '@allbluecn/ui/select'
25
+ import { ALL_ACTIONS, ALL_RESOURCES, LEVELS, type ResourcePermission } from './types'
26
+
27
+ export function CreateRoleDialog({
28
+ open,
29
+ onOpenChange,
30
+ workspaceId,
31
+ i18nLang,
32
+ onCreated,
33
+ }: {
34
+ open: boolean
35
+ onOpenChange: (v: boolean) => void
36
+ workspaceId: string
37
+ i18nLang: string
38
+ onCreated: () => void
39
+ }): React.ReactElement {
40
+ const createRole = useServerFn(createRoleFn)
41
+ const [name, setName] = useState('')
42
+ const [permissions, setPermissions] = useState<ResourcePermission>({})
43
+ const isZh = i18nLang === 'zh'
44
+
45
+ useEffect(() => {
46
+ if (open) {
47
+ setName('')
48
+ setPermissions({})
49
+ }
50
+ }, [open])
51
+
52
+ const handleLevelChange = useCallback((resource: string, action: string, value: string) => {
53
+ const level = (value as 'none' | 'read' | 'write' | 'admin') || 'none'
54
+ setPermissions((prev) => ({ ...prev, [`${resource}:${action}`]: level }))
55
+ }, [])
56
+
57
+ const handleSubmit = async () => {
58
+ if (!name.trim()) {
59
+ toast.warning(m.enterprise_permissions_toast_nameRequired())
60
+ return
61
+ }
62
+ await createRole({
63
+ data: {
64
+ workspaceId,
65
+ name: name.trim(),
66
+ permissions,
67
+ },
68
+ })
69
+ toast.success(m.enterprise_permissions_toast_created({ name: name.trim() }))
70
+ onOpenChange(false)
71
+ onCreated()
72
+ }
73
+
74
+ return (
75
+ <Dialog open={open} onOpenChange={onOpenChange}>
76
+ <DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto">
77
+ <DialogHeader>
78
+ <DialogTitle>{m.enterprise_permissions_createRole()}</DialogTitle>
79
+ <DialogDescription>{m.enterprise_permissions_createRoleDesc()}</DialogDescription>
80
+ </DialogHeader>
81
+
82
+ <div className="space-y-4">
83
+ <div className="grid gap-2">
84
+ <Label htmlFor="role-name">{m.enterprise_permissions_roleName()}</Label>
85
+ <Input
86
+ id="role-name"
87
+ value={name}
88
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => setName(e.target.value)}
89
+ placeholder={m.enterprise_permissions_roleNamePlaceholder()}
90
+ autoFocus
91
+ />
92
+ </div>
93
+
94
+ <div className="space-y-3">
95
+ <Label>{m.enterprise_permissions_permissionsMatrix()}</Label>
96
+ {ALL_RESOURCES.map((resource) => (
97
+ <div key={resource.key} className="space-y-2">
98
+ <span className="text-sm font-medium">{isZh ? resource.labelZh : resource.labelEn}</span>
99
+ <div className="grid grid-cols-4 gap-2">
100
+ {ALL_ACTIONS.map((action) => (
101
+ <Select
102
+ key={action.key}
103
+ value={permissions[`${resource.key}:${action.key}`] || 'none'}
104
+ onValueChange={(value) => handleLevelChange(resource.key, action.key, value ?? "")}
105
+ >
106
+ <SelectTrigger className="h-8">
107
+ <SelectValue>
108
+ {isZh ? resource.labelEn : resource.labelZh} / {isZh ? action.labelZh : action.labelEn}
109
+ </SelectValue>
110
+ </SelectTrigger>
111
+ <SelectContent>
112
+ {LEVELS.map((level) => (
113
+ <SelectItem key={level.value} value={level.value}>
114
+ {isZh ? level.labelZh : level.labelEn}
115
+ </SelectItem>
116
+ ))}
117
+ </SelectContent>
118
+ </Select>
119
+ ))}
120
+ </div>
121
+ </div>
122
+ ))}
123
+ </div>
124
+ </div>
125
+
126
+ <DialogFooter>
127
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
128
+ {m.enterprise_permissions_dialog_cancel()}
129
+ </Button>
130
+ <Button onClick={handleSubmit}>{m.enterprise_permissions_dialog_create()}</Button>
131
+ </DialogFooter>
132
+ </DialogContent>
133
+ </Dialog>
134
+ )
135
+ }
@@ -0,0 +1,49 @@
1
+ export type ResourcePermission = Record<string, 'admin' | 'write' | 'read' | 'none'>
2
+
3
+ export type RoleItem = {
4
+ id: string
5
+ name: string
6
+ permissions: ResourcePermission
7
+ isSystem: boolean
8
+ _count: { assignments: number }
9
+ }
10
+
11
+ export type AssignmentItem = {
12
+ id: string
13
+ roleId: string
14
+ userId: string
15
+ user: {
16
+ id: string
17
+ username: string
18
+ email: string
19
+ }
20
+ createdAt: string | Date
21
+ }
22
+
23
+ export type SearchUser = {
24
+ id: string
25
+ username: string
26
+ email: string
27
+ }
28
+
29
+ export const ALL_RESOURCES: { key: string; labelZh: string; labelEn: string }[] = [
30
+ { key: 'knowledge', labelZh: '知识库', labelEn: 'Knowledge' },
31
+ { key: 'story', labelZh: '故事', labelEn: 'Story' },
32
+ { key: 'user', labelZh: '用户', labelEn: 'User' },
33
+ { key: 'setting', labelZh: '设置', labelEn: 'Setting' },
34
+ { key: 'billing', labelZh: '计费', labelEn: 'Billing' },
35
+ ]
36
+
37
+ export const ALL_ACTIONS: { key: string; labelZh: string; labelEn: string }[] = [
38
+ { key: 'read', labelZh: '读', labelEn: 'Read' },
39
+ { key: 'write', labelZh: '写', labelEn: 'Write' },
40
+ { key: 'delete', labelZh: '删', labelEn: 'Delete' },
41
+ { key: 'publish', labelZh: '发布', labelEn: 'Publish' },
42
+ ]
43
+
44
+ export const LEVELS: { value: 'none' | 'read' | 'write' | 'admin'; labelZh: string; labelEn: string }[] = [
45
+ { value: 'none', labelZh: '无', labelEn: 'None' },
46
+ { value: 'read', labelZh: '读', labelEn: 'Read' },
47
+ { value: 'write', labelZh: '写', labelEn: 'Write' },
48
+ { value: 'admin', labelZh: '管理', labelEn: 'Admin' },
49
+ ]
@@ -16,12 +16,12 @@
16
16
  // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
18
 
19
- import { Badge } from "@/components/ui/badge";
20
- import { Button } from "@/components/ui/button";
19
+ import { Badge } from "@allbluecn/ui/badge";
20
+ import { Button } from "@allbluecn/ui/button";
21
21
  import { EyeIcon } from "lucide-react";
22
- import { UserAvatar } from "@allbluecn/web-app/user-avatar";
22
+ import { UserAvatar } from "@allbluecn/ui/components/user-avatar";
23
23
  import type { AvatarConfig } from "@allbluecn/kernel";
24
- import type { AdminColumnDef } from "@/components/data-table/data-table";
24
+ import type { AdminColumnDef } from "@allbluecn/ui/data-table";
25
25
 
26
26
  export type UserRow = {
27
27
  id: string;
@@ -36,7 +36,7 @@ export type UserRow = {
36
36
  updatedAt: Date;
37
37
  lastLoginAt: Date | null;
38
38
  _count: {
39
- prds: number;
39
+ designs: number;
40
40
  anyhubMarketApps: number;
41
41
  reviews: number;
42
42
  };
@@ -112,7 +112,7 @@ export function getUserColumns(onView: (user: UserRow) => void): AdminColumnDef<
112
112
  {
113
113
  id: "prdCount",
114
114
  header: "PRD",
115
- cell: ({ row }: any) => <span className="text-muted-foreground">{row.original._count.prds}</span>,
115
+ cell: ({ row }: any) => <span className="text-muted-foreground">{row.original._count.designs}</span>,
116
116
  },
117
117
  {
118
118
  id: "pluginCount",