@allbluecn/admin-app 0.1.0

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 (276) hide show
  1. package/package.json +86 -0
  2. package/src/client.tsx +31 -0
  3. package/src/components/admin/admin-user-menu.tsx +90 -0
  4. package/src/components/admin/developer-log.tsx +356 -0
  5. package/src/components/ai-provider/model-list-editor.tsx +147 -0
  6. package/src/components/ai-provider/provider-form.tsx +434 -0
  7. package/src/components/ai-provider/provider-icon.tsx +43 -0
  8. package/src/components/ai-provider/provider-logo.tsx +87 -0
  9. package/src/components/ai-provider/provider-table.tsx +258 -0
  10. package/src/components/auth/admin-login-form.tsx +214 -0
  11. package/src/components/auth/auth-brand-panel.tsx +60 -0
  12. package/src/components/auth/auth-header.tsx +38 -0
  13. package/src/components/auth/auth-layout.tsx +66 -0
  14. package/src/components/auth/auth-providers.tsx +34 -0
  15. package/src/components/auth/icon-input.tsx +46 -0
  16. package/src/components/auth/oauth-section.tsx +51 -0
  17. package/src/components/auth/password-input.tsx +67 -0
  18. package/src/components/common/page-header.tsx +42 -0
  19. package/src/components/common/placeholder-page.tsx +42 -0
  20. package/src/components/component/component-form-dialog.tsx +348 -0
  21. package/src/components/component/component-import-dialog.tsx +235 -0
  22. package/src/components/component/component-table.tsx +153 -0
  23. package/src/components/data-table/data-table-pagination.tsx +114 -0
  24. package/src/components/data-table/data-table.tsx +218 -0
  25. package/src/components/data-table/table-detail-layout.tsx +99 -0
  26. package/src/components/dependency/changelog-dialog.tsx +108 -0
  27. package/src/components/dependency/columns.tsx +232 -0
  28. package/src/components/dependency/dependency-table.tsx +368 -0
  29. package/src/components/dependency/edit-dialog.tsx +200 -0
  30. package/src/components/knowledge-public/bookmark-form-dialog.tsx +215 -0
  31. package/src/components/knowledge-public/compliance-form-dialog.tsx +308 -0
  32. package/src/components/knowledge-public/legal-form-dialog.tsx +315 -0
  33. package/src/components/layout/sidebar-02/app-sidebar.tsx +160 -0
  34. package/src/components/layout/sidebar-02/index.tsx +33 -0
  35. package/src/components/layout/sidebar-02/logo.tsx +40 -0
  36. package/src/components/layout/sidebar-02/nav-main.tsx +153 -0
  37. package/src/components/layout/sidebar-02/nav-notifications.tsx +78 -0
  38. package/src/components/layout/sidebar-02/team-switcher.tsx +106 -0
  39. package/src/components/magicui/animated-theme-toggler.tsx +272 -0
  40. package/src/components/magicui/rainbow-button.tsx +78 -0
  41. package/src/components/market/columns.tsx +151 -0
  42. package/src/components/market/market-app-form.tsx +237 -0
  43. package/src/components/market/market-app-list.tsx +156 -0
  44. package/src/components/monitoring/error-list.tsx +143 -0
  45. package/src/components/monitoring/health-cards.tsx +152 -0
  46. package/src/components/monitoring/metrics-stats.tsx +184 -0
  47. package/src/components/monitoring/request-log-table.tsx +179 -0
  48. package/src/components/providers/theme-toggle.tsx +59 -0
  49. package/src/components/settings/avatar/avatar-config.ts +46 -0
  50. package/src/components/ui/alert-dialog.tsx +216 -0
  51. package/src/components/ui/alert.tsx +76 -0
  52. package/src/components/ui/avatar.tsx +129 -0
  53. package/src/components/ui/badge.tsx +66 -0
  54. package/src/components/ui/button.tsx +84 -0
  55. package/src/components/ui/card.tsx +120 -0
  56. package/src/components/ui/checkbox.tsx +50 -0
  57. package/src/components/ui/collapsible.tsx +50 -0
  58. package/src/components/ui/dialog.tsx +185 -0
  59. package/src/components/ui/dropdown-menu.tsx +286 -0
  60. package/src/components/ui/field.tsx +255 -0
  61. package/src/components/ui/input-group.tsx +171 -0
  62. package/src/components/ui/input.tsx +36 -0
  63. package/src/components/ui/label.tsx +41 -0
  64. package/src/components/ui/pagination.tsx +146 -0
  65. package/src/components/ui/select.tsx +209 -0
  66. package/src/components/ui/separator.tsx +45 -0
  67. package/src/components/ui/sheet.tsx +164 -0
  68. package/src/components/ui/sidebar.tsx +719 -0
  69. package/src/components/ui/skeleton.tsx +30 -0
  70. package/src/components/ui/sonner.tsx +37 -0
  71. package/src/components/ui/switch.tsx +48 -0
  72. package/src/components/ui/table.tsx +133 -0
  73. package/src/components/ui/textarea.tsx +35 -0
  74. package/src/components/ui/tooltip.tsx +74 -0
  75. package/src/components/user/columns.tsx +153 -0
  76. package/src/components/user/settings-activity-log.tsx +358 -0
  77. package/src/components/user/settings-profile/constants.ts +42 -0
  78. package/src/components/user/settings-profile/types.ts +47 -0
  79. package/src/components/user/settings-profile.tsx +664 -0
  80. package/src/components/user/user-create-dialog.tsx +137 -0
  81. package/src/components/user/user-filters.tsx +117 -0
  82. package/src/hooks/use-mobile.ts +36 -0
  83. package/src/instrument.client.ts +34 -0
  84. package/src/lib/anyhub/capabilities.ts +59 -0
  85. package/src/lib/anyhub/types.ts +43 -0
  86. package/src/lib/auth-client.ts +83 -0
  87. package/src/lib/auth-utils.ts +24 -0
  88. package/src/lib/auth.config.ts +264 -0
  89. package/src/lib/avatar.ts +41 -0
  90. package/src/lib/i18n/client.ts +32 -0
  91. package/src/lib/i18n/config.ts +40 -0
  92. package/src/lib/i18n/detect.ts +43 -0
  93. package/src/lib/i18n/locales/en/admin-ai.json +128 -0
  94. package/src/lib/i18n/locales/en/admin-billing.json +56 -0
  95. package/src/lib/i18n/locales/en/admin-users.json +88 -0
  96. package/src/lib/i18n/locales/en/common.json +877 -0
  97. package/src/lib/i18n/locales/en/enterprise.json +208 -0
  98. package/src/lib/i18n/locales/zh/admin-ai.json +128 -0
  99. package/src/lib/i18n/locales/zh/admin-billing.json +56 -0
  100. package/src/lib/i18n/locales/zh/admin-users.json +88 -0
  101. package/src/lib/i18n/locales/zh/common.json +877 -0
  102. package/src/lib/i18n/locales/zh/enterprise.json +208 -0
  103. package/src/lib/i18n/server.ts +18 -0
  104. package/src/lib/i18n/types.ts +5 -0
  105. package/src/lib/monitoring-benchmarks.ts +85 -0
  106. package/src/lib/monitoring-descriptions.ts +78 -0
  107. package/src/lib/prisma.ts +18 -0
  108. package/src/lib/router-context.ts +38 -0
  109. package/src/lib/session.ts +38 -0
  110. package/src/lib/utils.ts +23 -0
  111. package/src/providers/index.tsx +45 -0
  112. package/src/routeTree.gen.ts +1199 -0
  113. package/src/router.tsx +51 -0
  114. package/src/routes/__root.tsx +170 -0
  115. package/src/routes/_nav/ai-providers/$id/edit/route.lazy.tsx +74 -0
  116. package/src/routes/_nav/ai-providers/$id/edit/route.tsx +35 -0
  117. package/src/routes/_nav/ai-providers/index/route.lazy.tsx +33 -0
  118. package/src/routes/_nav/ai-providers/new/route.lazy.tsx +31 -0
  119. package/src/routes/_nav/ai-providers/new/route.tsx +20 -0
  120. package/src/routes/_nav/ai-providers/route.lazy.tsx +26 -0
  121. package/src/routes/_nav/ai-providers/route.tsx +31 -0
  122. package/src/routes/_nav/apps/route.lazy.tsx +30 -0
  123. package/src/routes/_nav/apps/route.tsx +20 -0
  124. package/src/routes/_nav/audit/route.lazy.tsx +100 -0
  125. package/src/routes/_nav/audit/route.tsx +25 -0
  126. package/src/routes/_nav/billing/invoices/route.lazy.tsx +86 -0
  127. package/src/routes/_nav/billing/invoices/route.tsx +25 -0
  128. package/src/routes/_nav/billing/route.lazy.tsx +48 -0
  129. package/src/routes/_nav/billing/route.tsx +27 -0
  130. package/src/routes/_nav/billing/subscriptions/$subscriptionId/route.lazy.tsx +160 -0
  131. package/src/routes/_nav/billing/subscriptions/$subscriptionId/route.tsx +25 -0
  132. package/src/routes/_nav/billing/subscriptions/route.lazy.tsx +113 -0
  133. package/src/routes/_nav/billing/subscriptions/route.tsx +28 -0
  134. package/src/routes/_nav/compliance/route.lazy.tsx +184 -0
  135. package/src/routes/_nav/compliance/route.tsx +25 -0
  136. package/src/routes/_nav/components/route.lazy.tsx +184 -0
  137. package/src/routes/_nav/components/route.tsx +40 -0
  138. package/src/routes/_nav/dashboard/route.lazy.tsx +33 -0
  139. package/src/routes/_nav/dashboard/route.tsx +20 -0
  140. package/src/routes/_nav/dependencies/route.lazy.tsx +66 -0
  141. package/src/routes/_nav/dependencies/route.tsx +32 -0
  142. package/src/routes/_nav/developer-logs/route.lazy.tsx +6 -0
  143. package/src/routes/_nav/developer-logs/route.tsx +14 -0
  144. package/src/routes/_nav/docs/route.lazy.tsx +30 -0
  145. package/src/routes/_nav/docs/route.tsx +20 -0
  146. package/src/routes/_nav/knowledge-public/bookmarks/route.lazy.tsx +355 -0
  147. package/src/routes/_nav/knowledge-public/bookmarks/route.tsx +40 -0
  148. package/src/routes/_nav/knowledge-public/compliance/route.lazy.tsx +245 -0
  149. package/src/routes/_nav/knowledge-public/compliance/route.tsx +40 -0
  150. package/src/routes/_nav/knowledge-public/legal/route.lazy.tsx +242 -0
  151. package/src/routes/_nav/knowledge-public/legal/route.tsx +40 -0
  152. package/src/routes/_nav/licenses/$licenseId/route.lazy.tsx +235 -0
  153. package/src/routes/_nav/licenses/$licenseId/route.tsx +25 -0
  154. package/src/routes/_nav/licenses/new/route.lazy.tsx +237 -0
  155. package/src/routes/_nav/licenses/route.lazy.tsx +143 -0
  156. package/src/routes/_nav/licenses/route.tsx +25 -0
  157. package/src/routes/_nav/market/route.lazy.tsx +31 -0
  158. package/src/routes/_nav/market/route.tsx +20 -0
  159. package/src/routes/_nav/monitoring/ai-usage/route.lazy.tsx +305 -0
  160. package/src/routes/_nav/monitoring/ai-usage/route.tsx +3 -0
  161. package/src/routes/_nav/monitoring/errors/route.lazy.tsx +111 -0
  162. package/src/routes/_nav/monitoring/health/route.lazy.tsx +260 -0
  163. package/src/routes/_nav/monitoring/logs/route.lazy.tsx +165 -0
  164. package/src/routes/_nav/monitoring/metrics/route.lazy.tsx +240 -0
  165. package/src/routes/_nav/monitoring/overview/route.lazy.tsx +138 -0
  166. package/src/routes/_nav/monitoring/route.lazy.tsx +68 -0
  167. package/src/routes/_nav/monitoring/route.tsx +20 -0
  168. package/src/routes/_nav/monitoring/sentry/route.lazy.tsx +399 -0
  169. package/src/routes/_nav/monitoring/sentry/route.tsx +20 -0
  170. package/src/routes/_nav/permissions/route.lazy.tsx +504 -0
  171. package/src/routes/_nav/permissions/route.tsx +26 -0
  172. package/src/routes/_nav/route.lazy.tsx +31 -0
  173. package/src/routes/_nav/route.tsx +34 -0
  174. package/src/routes/_nav/settings/route.lazy.tsx +30 -0
  175. package/src/routes/_nav/settings/route.tsx +20 -0
  176. package/src/routes/_nav/sso/route.lazy.tsx +145 -0
  177. package/src/routes/_nav/sso/route.tsx +25 -0
  178. package/src/routes/_nav/users/$userId/route.lazy.tsx +432 -0
  179. package/src/routes/_nav/users/$userId/route.tsx +27 -0
  180. package/src/routes/_nav/users/-components/users-table.tsx +38 -0
  181. package/src/routes/_nav/users/route.lazy.tsx +74 -0
  182. package/src/routes/_nav/users/route.tsx +43 -0
  183. package/src/routes/api/compliance-download/$jobId.ts +35 -0
  184. package/src/routes/api/observability/errors.ts +41 -0
  185. package/src/routes/api/observability/health.ts +30 -0
  186. package/src/routes/api.auth.$.ts +31 -0
  187. package/src/routes/api.sentry-test.ts +33 -0
  188. package/src/routes/index/route.lazy.tsx +22 -0
  189. package/src/routes/index/route.tsx +24 -0
  190. package/src/routes/login/route.lazy.tsx +46 -0
  191. package/src/routes/login/route.tsx +28 -0
  192. package/src/routes/sentry-tunnel.ts +61 -0
  193. package/src/routes/setup/route.lazy.tsx +94 -0
  194. package/src/routes/setup/route.tsx +28 -0
  195. package/src/server/__tests__/admin-ai-alerts.test.ts +113 -0
  196. package/src/server/__tests__/licenses-issue.test.ts +98 -0
  197. package/src/server/admin-ai-alerts.ts +177 -0
  198. package/src/server/admin-ai-usage.ts +134 -0
  199. package/src/server/auth-helpers.ts +51 -0
  200. package/src/server/entitlements.ts +24 -0
  201. package/src/server/knowledge-public/canonical-id.ts +55 -0
  202. package/src/server/knowledge-public/github-sync.ts +86 -0
  203. package/src/server/license-issue.ts +78 -0
  204. package/src/server/middlewares/request.ts +26 -0
  205. package/src/server/observability/debug-middleware.ts +39 -0
  206. package/src/server/observability/error-reporter.ts +99 -0
  207. package/src/server/observability/health-check.ts +140 -0
  208. package/src/server/observability/index.ts +33 -0
  209. package/src/server/observability/logger.ts +49 -0
  210. package/src/server/observability/metrics.ts +105 -0
  211. package/src/server/observability/request-middleware.ts +125 -0
  212. package/src/server/observability/timing.ts +42 -0
  213. package/src/server/pro-services.ts +32 -0
  214. package/src/server/serverFns/admin-billing.ts +94 -0
  215. package/src/server/serverFns/ai-provider/create.ts +92 -0
  216. package/src/server/serverFns/ai-provider/delete.ts +42 -0
  217. package/src/server/serverFns/ai-provider/get.ts +35 -0
  218. package/src/server/serverFns/ai-provider/list.ts +92 -0
  219. package/src/server/serverFns/ai-provider/update.ts +73 -0
  220. package/src/server/serverFns/anyhub/market.ts +64 -0
  221. package/src/server/serverFns/audit.ts +57 -0
  222. package/src/server/serverFns/auth/session.ts +26 -0
  223. package/src/server/serverFns/auth/setup.ts +73 -0
  224. package/src/server/serverFns/compliance.ts +85 -0
  225. package/src/server/serverFns/components/import.ts +197 -0
  226. package/src/server/serverFns/components/list.ts +51 -0
  227. package/src/server/serverFns/components/mutations.ts +84 -0
  228. package/src/server/serverFns/dependency/helpers.ts +245 -0
  229. package/src/server/serverFns/dependency/list.ts +127 -0
  230. package/src/server/serverFns/dependency/sync.ts +123 -0
  231. package/src/server/serverFns/dependency/update.ts +51 -0
  232. package/src/server/serverFns/developer-log-auto-generate.ts +176 -0
  233. package/src/server/serverFns/developer-logs.ts +132 -0
  234. package/src/server/serverFns/knowledge-public/bookmarks/create.ts +53 -0
  235. package/src/server/serverFns/knowledge-public/bookmarks/delete.ts +31 -0
  236. package/src/server/serverFns/knowledge-public/bookmarks/detail.ts +32 -0
  237. package/src/server/serverFns/knowledge-public/bookmarks/list.ts +66 -0
  238. package/src/server/serverFns/knowledge-public/bookmarks/sync-all-github.ts +56 -0
  239. package/src/server/serverFns/knowledge-public/bookmarks/update.ts +50 -0
  240. package/src/server/serverFns/knowledge-public/compliance/create.ts +58 -0
  241. package/src/server/serverFns/knowledge-public/compliance/delete.ts +31 -0
  242. package/src/server/serverFns/knowledge-public/compliance/detail.ts +32 -0
  243. package/src/server/serverFns/knowledge-public/compliance/list.ts +59 -0
  244. package/src/server/serverFns/knowledge-public/compliance/update.ts +53 -0
  245. package/src/server/serverFns/knowledge-public/legal/create.ts +62 -0
  246. package/src/server/serverFns/knowledge-public/legal/delete.ts +31 -0
  247. package/src/server/serverFns/knowledge-public/legal/detail.ts +32 -0
  248. package/src/server/serverFns/knowledge-public/legal/list.ts +60 -0
  249. package/src/server/serverFns/knowledge-public/legal/update.ts +55 -0
  250. package/src/server/serverFns/licenses.ts +92 -0
  251. package/src/server/serverFns/locale.ts +10 -0
  252. package/src/server/serverFns/monitoring/check-ai-alerts.ts +13 -0
  253. package/src/server/serverFns/monitoring/get-ai-usage-overview.ts +25 -0
  254. package/src/server/serverFns/monitoring/get-errors.ts +43 -0
  255. package/src/server/serverFns/monitoring/get-health-settings.ts +35 -0
  256. package/src/server/serverFns/monitoring/get-health.ts +55 -0
  257. package/src/server/serverFns/monitoring/get-metrics.ts +46 -0
  258. package/src/server/serverFns/monitoring/get-request-logs.ts +66 -0
  259. package/src/server/serverFns/monitoring/report-error.ts +55 -0
  260. package/src/server/serverFns/monitoring/reset.ts +70 -0
  261. package/src/server/serverFns/permissions.ts +214 -0
  262. package/src/server/serverFns/sentry/config.ts +88 -0
  263. package/src/server/serverFns/sso-config.ts +83 -0
  264. package/src/server/serverFns/user/create.ts +40 -0
  265. package/src/server/serverFns/user/delete.ts +29 -0
  266. package/src/server/serverFns/user/detail.ts +45 -0
  267. package/src/server/serverFns/user/list.ts +56 -0
  268. package/src/server/serverFns/user/reset-password.ts +28 -0
  269. package/src/server/serverFns/user/update-role.ts +26 -0
  270. package/src/server/serverFns/user/update-status.ts +26 -0
  271. package/src/server.ts +27 -0
  272. package/src/start.ts +85 -0
  273. package/src/styles/_keyframe-animations.scss +91 -0
  274. package/src/styles/_variables.scss +296 -0
  275. package/src/styles/globals.css +200 -0
  276. package/src/types/start-authjs.d.ts +27 -0
@@ -0,0 +1,160 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import { useTranslation } from 'react-i18next'
19
+ import {
20
+ Sidebar,
21
+ SidebarContent,
22
+ SidebarFooter,
23
+ SidebarHeader,
24
+ SidebarTrigger,
25
+ useSidebar,
26
+ } from "@/components/ui/sidebar";
27
+ import { cn } from "@/lib/utils";
28
+ import { motion } from "motion/react";
29
+ import {
30
+ Home,
31
+ Layers,
32
+ Package2,
33
+ Settings,
34
+ ShoppingBag,
35
+ Users,
36
+ FileText,
37
+ Bot,
38
+ Boxes,
39
+ Library,
40
+ Scale,
41
+ ShieldCheck,
42
+ Bookmark,
43
+ BugPlay,
44
+ Activity,
45
+ CreditCard,
46
+ Shield,
47
+ ScrollText,
48
+ } from "lucide-react";
49
+ import { Logo } from "@/components/layout/sidebar-02/logo";
50
+ import type { Route } from "./nav-main";
51
+ import DashboardNavigation from "@/components/layout/sidebar-02/nav-main";
52
+ import { NotificationsPopover } from "@/components/layout/sidebar-02/nav-notifications";
53
+ import { AdminUserMenu } from "@/components/admin/admin-user-menu";
54
+
55
+ function buildDashboardRoutes(t: (key: string) => string): Route[] {
56
+ return [
57
+ { id: "home", title: t('nav.dashboard'), icon: <Home className="size-4" />, link: "/dashboard" },
58
+ { id: "users", title: t('nav.users'), icon: <Users className="size-4" />, link: "/users" },
59
+ { id: "docs", title: t('nav.docs'), icon: <FileText className="size-4" />, link: "/docs" },
60
+ { id: "apps", title: t('nav.apps'), icon: <Package2 className="size-4" />, link: "/apps" },
61
+ {
62
+ id: "knowledge-public", title: t('nav.knowledgePublic.title'), icon: <Library className="size-4" />, link: "#",
63
+ subs: [
64
+ { title: t('nav.knowledgePublic.laws'), link: "/knowledge-public/legal", icon: <Scale className="size-4" /> },
65
+ { title: t('nav.knowledgePublic.compliance'), link: "/knowledge-public/compliance", icon: <ShieldCheck className="size-4" /> },
66
+ { title: t('nav.knowledgePublic.bookmarks'), link: "/knowledge-public/bookmarks", icon: <Bookmark className="size-4" /> },
67
+ ],
68
+ },
69
+ { id: "ai-providers", title: t('nav.aiProviders'), icon: <Bot className="size-4" />, link: "/ai-providers" },
70
+ { id: "components", title: t('nav.components'), icon: <Layers className="size-4" />, link: "/components" },
71
+ { id: "dependencies", title: t('nav.dependencies'), icon: <Boxes className="size-4" />, link: "/dependencies" },
72
+ { id: "developer-logs", title: t('nav.developerLogs'), icon: <ScrollText className="size-4" />, link: "/developer-logs" },
73
+ { id: "market", title: t('nav.market'), icon: <ShoppingBag className="size-4" />, link: "/market" },
74
+ {
75
+ id: "billing", title: t('nav.billing.title'), icon: <CreditCard className="size-4" />, link: "#",
76
+ subs: [
77
+ { title: t('nav.billing.subscriptions'), link: "/billing/subscriptions" },
78
+ { title: t('nav.billing.invoices'), link: "/billing/invoices" },
79
+ ],
80
+ },
81
+ {
82
+ id: "enterprise", title: t('nav.enterprise.title'), icon: <Shield className="size-4" />, link: "#",
83
+ subs: [
84
+ { title: t('nav.enterprise.licenses'), link: "/licenses" },
85
+ { title: t('nav.enterprise.audit'), link: "/audit" },
86
+ { title: t('nav.enterprise.sso'), link: "/sso" },
87
+ { title: t('nav.enterprise.permissions'), link: "/permissions" },
88
+ { title: t('nav.enterprise.compliance'), link: "/compliance" },
89
+ ],
90
+ },
91
+ {
92
+ id: "monitoring", title: t('nav.monitoring.title'), icon: <Activity className="size-4" />, link: "#",
93
+ subs: [
94
+ { title: t('nav.monitoring.overview'), link: "/monitoring/overview" },
95
+ { title: t('nav.monitoring.logs'), link: "/monitoring/logs" },
96
+ { title: t('nav.monitoring.errors'), link: "/monitoring/errors" },
97
+ { title: t('nav.monitoring.metrics'), link: "/monitoring/metrics" },
98
+ { title: t('nav.monitoring.health'), link: "/monitoring/health" },
99
+ { title: t('nav.monitoring.sentry'), link: "/monitoring/sentry" },
100
+ ],
101
+ },
102
+ { id: "settings", title: t('nav.settings'), icon: <Settings className="size-4" />, link: "/settings" },
103
+ ]
104
+ }
105
+
106
+ const teams = [
107
+ { id: "1", name: "Alpha Inc.", logo: Logo, plan: "Free" },
108
+ { id: "2", name: "Beta Corp.", logo: Logo, plan: "Free" },
109
+ { id: "3", name: "Gamma Tech", logo: Logo, plan: "Free" },
110
+ ];
111
+
112
+ export function DashboardSidebar({ routes: propRoutes }: { routes?: Route[] }) {
113
+ const { t } = useTranslation('common')
114
+ const { state } = useSidebar();
115
+ const isCollapsed = state === "collapsed";
116
+ const routes = propRoutes || buildDashboardRoutes(t);
117
+
118
+ return (
119
+ <Sidebar variant="inset" collapsible="icon">
120
+ <SidebarHeader
121
+ className={cn(
122
+ "flex md:pt-3.5",
123
+ isCollapsed
124
+ ? "flex-row items-center justify-between gap-y-4 md:flex-col md:items-start md:justify-start"
125
+ : "flex-row items-center justify-between"
126
+ )}
127
+ >
128
+ <a href="#" className="flex items-center gap-2">
129
+ <Logo className="h-8 w-8" />
130
+ {!isCollapsed && (
131
+ <span className="font-semibold text-black dark:text-white">
132
+ Admin
133
+ </span>
134
+ )}
135
+ </a>
136
+
137
+ <motion.div
138
+ key={isCollapsed ? "header-collapsed" : "header-expanded"}
139
+ className={cn(
140
+ "flex items-center gap-2",
141
+ isCollapsed ? "flex-row md:flex-col-reverse" : "flex-row"
142
+ )}
143
+ initial={{ opacity: 0 }}
144
+ animate={{ opacity: 1 }}
145
+ transition={{ duration: 0.8 }}
146
+ >
147
+ <NotificationsPopover notifications={[]} />
148
+ {!isCollapsed && <SidebarTrigger />}
149
+ {isCollapsed && <SidebarTrigger />}
150
+ </motion.div>
151
+ </SidebarHeader>
152
+ <SidebarContent className="gap-4 px-2 py-4">
153
+ <DashboardNavigation routes={routes} />
154
+ </SidebarContent>
155
+ <SidebarFooter className="px-2">
156
+ <AdminUserMenu />
157
+ </SidebarFooter>
158
+ </Sidebar>
159
+ );
160
+ }
@@ -0,0 +1,33 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import type { ReactNode } from "react"
19
+ import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
20
+ import { DashboardSidebar } from "@/components/layout/sidebar-02/app-sidebar";
21
+
22
+ export default function Sidebar02({ children }: { children?: ReactNode }) {
23
+ return (
24
+ <SidebarProvider>
25
+ <div className="relative flex h-dvh w-full overflow-hidden">
26
+ <DashboardSidebar />
27
+ <SidebarInset className="flex min-h-0 flex-col overflow-hidden">
28
+ <div className="min-h-0 flex-1 flex flex-col overflow-hidden">{children}</div>
29
+ </SidebarInset>
30
+ </div>
31
+ </SidebarProvider>
32
+ );
33
+ }
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import { SVGProps } from "react";
19
+
20
+ export const Logo = (props: SVGProps<SVGSVGElement>) => (
21
+ <svg
22
+ fill="currentColor"
23
+ height="48"
24
+ viewBox="0 0 40 48"
25
+ width="40"
26
+ {...props}
27
+ >
28
+ <clipPath id="a">
29
+ <path d="m0 0h40v48h-40z" />
30
+ </clipPath>
31
+ <g clipPath="url(#a)">
32
+ <path d="m25.0887 5.05386-3.933-1.05386-3.3145 12.3696-2.9923-11.16736-3.9331 1.05386 3.233 12.0655-8.05262-8.0526-2.87919 2.8792 8.83271 8.8328-10.99975-2.9474-1.05385625 3.933 12.01860625 3.2204c-.1376-.5935-.2104-1.2119-.2104-1.8473 0-4.4976 3.646-8.1436 8.1437-8.1436 4.4976 0 8.1436 3.646 8.1436 8.1436 0 .6313-.0719 1.2459-.2078 1.8359l10.9227 2.9267 1.0538-3.933-12.0664-3.2332 11.0005-2.9476-1.0539-3.933-12.0659 3.233 8.0526-8.0526-2.8792-2.87916-8.7102 8.71026z" />
33
+ <path d="m27.8723 26.2214c-.3372 1.4256-1.0491 2.7063-2.0259 3.7324l7.913 7.9131 2.8792-2.8792z" />
34
+ <path d="m25.7665 30.0366c-.9886 1.0097-2.2379 1.7632-3.6389 2.1515l2.8794 10.746 3.933-1.0539z" />
35
+ <path d="m21.9807 32.2274c-.65.1671-1.3313.2559-2.0334.2559-.7522 0-1.4806-.102-2.1721-.2929l-2.882 10.7558 3.933 1.0538z" />
36
+ <path d="m17.6361 32.1507c-1.3796-.4076-2.6067-1.1707-3.5751-2.1833l-7.9325 7.9325 2.87919 2.8792z" />
37
+ <path d="m13.9956 29.8973c-.9518-1.019-1.6451-2.2826-1.9751-3.6862l-10.95836 2.9363 1.05385 3.933z" />
38
+ </g>
39
+ </svg>
40
+ );
@@ -0,0 +1,153 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+
19
+ import {
20
+ Collapsible,
21
+ CollapsibleContent,
22
+ CollapsibleTrigger,
23
+ } from "@/components/ui/collapsible";
24
+ import {
25
+ SidebarMenu,
26
+ SidebarMenuButton,
27
+ SidebarMenuItem,
28
+ SidebarMenuSub,
29
+ SidebarMenuSubButton,
30
+ SidebarMenuItem as SidebarMenuSubItem,
31
+ useSidebar,
32
+ } from "@/components/ui/sidebar";
33
+ import { cn } from "@/lib/utils";
34
+ import { ChevronDown, ChevronUp } from "lucide-react";
35
+ import { Link } from "@tanstack/react-router";
36
+ import { useLocation } from "@tanstack/react-router";
37
+ import type React from "react";
38
+ import { useState } from "react";
39
+
40
+ export type Route = {
41
+ id: string;
42
+ title: string;
43
+ icon?: React.ReactNode;
44
+ link: string;
45
+ subs?: {
46
+ title: string;
47
+ link: string;
48
+ icon?: React.ReactNode;
49
+ }[];
50
+ };
51
+
52
+ export default function DashboardNavigation({ routes }: { routes: Route[] }) {
53
+ const { state } = useSidebar();
54
+ const { pathname } = useLocation();
55
+ const isCollapsed = state === "collapsed";
56
+ const [openCollapsible, setOpenCollapsible] = useState<string | null>(null);
57
+
58
+ const isActive = (link: string) => {
59
+ if (link === "#") return false;
60
+ if (link === "/") return pathname === "/";
61
+ return pathname === link;
62
+ };
63
+
64
+ return (
65
+ <SidebarMenu className="space-y-1">
66
+ {routes.map((route) => {
67
+ const isOpen = !isCollapsed && openCollapsible === route.id;
68
+ const hasSubRoutes = !!route.subs?.length;
69
+ const active = isActive(route.link);
70
+
71
+ return (
72
+ <SidebarMenuItem key={route.id}>
73
+ {hasSubRoutes ? (
74
+ <Collapsible
75
+ open={isOpen}
76
+ onOpenChange={(open) =>
77
+ setOpenCollapsible(open ? route.id : null)
78
+ }
79
+ className="w-full"
80
+ >
81
+ <SidebarMenuButton
82
+ isActive={isOpen || active}
83
+ className={cn(isCollapsed && "justify-center")}
84
+ onClick={() => {
85
+ if (!isCollapsed) {
86
+ setOpenCollapsible(isOpen ? null : route.id)
87
+ }
88
+ }}
89
+ >
90
+ {route.icon}
91
+ {!isCollapsed && (
92
+ <span className="ml-2 flex-1 text-sm font-medium">
93
+ {route.title}
94
+ </span>
95
+ )}
96
+ {!isCollapsed && hasSubRoutes && (
97
+ <span className="ml-auto">
98
+ {isOpen ? (
99
+ <ChevronUp className="size-4" />
100
+ ) : (
101
+ <ChevronDown className="size-4" />
102
+ )}
103
+ </span>
104
+ )}
105
+ </SidebarMenuButton>
106
+
107
+ {!isCollapsed && (
108
+ <CollapsibleContent >
109
+ <SidebarMenuSub className="my-1 ml-3.5 ">
110
+ {route.subs?.map((subRoute) => (
111
+ <SidebarMenuSubItem
112
+ key={`${route.id}-${subRoute.title}`}
113
+ className="h-auto"
114
+ >
115
+ <SidebarMenuSubButton asChild isActive={isActive(subRoute.link)}>
116
+ <Link
117
+ to={subRoute.link}
118
+ className="flex items-center rounded-md px-4 py-1.5 text-sm font-medium"
119
+ >
120
+ {subRoute.icon}
121
+ {subRoute.title}
122
+ </Link>
123
+ </SidebarMenuSubButton>
124
+ </SidebarMenuSubItem>
125
+ ))}
126
+ </SidebarMenuSub>
127
+ </CollapsibleContent>
128
+ )}
129
+ </Collapsible>
130
+ ) : (
131
+ <SidebarMenuButton asChild tooltip={route.title} isActive={active} >
132
+ <Link
133
+ to={route.link}
134
+ className={cn(
135
+ "flex items-center rounded-lg px-2 ",
136
+ isCollapsed && "justify-center"
137
+ )}
138
+ >
139
+ {route.icon}
140
+ {!isCollapsed && (
141
+ <span className="ml-2 text-sm font-medium">
142
+ {route.title}
143
+ </span>
144
+ )}
145
+ </Link>
146
+ </SidebarMenuButton>
147
+ )}
148
+ </SidebarMenuItem>
149
+ );
150
+ })}
151
+ </SidebarMenu>
152
+ );
153
+ }
@@ -0,0 +1,78 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+
19
+ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
20
+ import { Button } from "@/components/ui/button";
21
+ import {
22
+ DropdownMenu,
23
+ DropdownMenuContent,
24
+ DropdownMenuItem,
25
+ DropdownMenuLabel,
26
+ DropdownMenuSeparator,
27
+ DropdownMenuTrigger,
28
+ } from "@/components/ui/dropdown-menu";
29
+ import { BellIcon } from "lucide-react";
30
+
31
+ type Notification = {
32
+ id: string;
33
+ avatar: string;
34
+ fallback: string;
35
+ text: string;
36
+ time: string;
37
+ };
38
+
39
+ export function NotificationsPopover({
40
+ notifications,
41
+ }: {
42
+ notifications: Notification[];
43
+ }) {
44
+ return (
45
+ <DropdownMenu>
46
+ <DropdownMenuTrigger asChild>
47
+ <Button
48
+ variant="ghost"
49
+ size="icon"
50
+ className="rounded-full"
51
+ aria-label="Open notifications"
52
+ >
53
+ <BellIcon className="size-5" />
54
+ </Button>
55
+ </DropdownMenuTrigger>
56
+ <DropdownMenuContent side="right" className="w-80 my-6">
57
+ <DropdownMenuLabel>Notifications</DropdownMenuLabel>
58
+ <DropdownMenuSeparator />
59
+ {notifications.map(({ id, avatar, fallback, text, time }) => (
60
+ <DropdownMenuItem key={id} className="flex items-start gap-3">
61
+ <Avatar className="size-8">
62
+ <AvatarImage src={avatar} alt="Avatar" />
63
+ <AvatarFallback>{fallback}</AvatarFallback>
64
+ </Avatar>
65
+ <div className="flex flex-col">
66
+ <span className="text-sm font-medium">{text}</span>
67
+ <span className="text-xs text-muted-foreground">{time}</span>
68
+ </div>
69
+ </DropdownMenuItem>
70
+ ))}
71
+ <DropdownMenuSeparator />
72
+ <DropdownMenuItem className="justify-center text-sm text-muted-foreground hover:text-primary">
73
+ View all notifications
74
+ </DropdownMenuItem>
75
+ </DropdownMenuContent>
76
+ </DropdownMenu>
77
+ );
78
+ }
@@ -0,0 +1,106 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+
19
+ import {
20
+ DropdownMenu,
21
+ DropdownMenuContent,
22
+ DropdownMenuItem,
23
+ DropdownMenuLabel,
24
+ DropdownMenuSeparator,
25
+ DropdownMenuShortcut,
26
+ DropdownMenuTrigger,
27
+ } from "@/components/ui/dropdown-menu";
28
+ import {
29
+ SidebarMenu,
30
+ SidebarMenuButton,
31
+ SidebarMenuItem,
32
+ useSidebar,
33
+ } from "@/components/ui/sidebar";
34
+ import { ChevronsUpDown, Plus } from "lucide-react";
35
+ import * as React from "react";
36
+
37
+ type Team = {
38
+ name: string;
39
+ logo: React.ElementType;
40
+ plan: string;
41
+ };
42
+
43
+ export function TeamSwitcher({ teams }: { teams: Team[] }) {
44
+ const { isMobile } = useSidebar();
45
+ const [activeTeam, setActiveTeam] = React.useState(teams[0]);
46
+
47
+ if (!activeTeam) return null;
48
+
49
+ const Logo = activeTeam.logo;
50
+
51
+ return (
52
+ <SidebarMenu>
53
+ <SidebarMenuItem>
54
+ <DropdownMenu>
55
+ <DropdownMenuTrigger asChild>
56
+ <SidebarMenuButton
57
+ size="lg"
58
+ className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
59
+ >
60
+ <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-background text-foreground">
61
+ <Logo className="size-4" />
62
+ </div>
63
+ <div className="grid flex-1 text-left text-sm leading-tight">
64
+ <span className="truncate font-semibold">
65
+ {activeTeam.name}
66
+ </span>
67
+ <span className="truncate text-xs">{activeTeam.plan}</span>
68
+ </div>
69
+ <ChevronsUpDown className="ml-auto" />
70
+ </SidebarMenuButton>
71
+ </DropdownMenuTrigger>
72
+ <DropdownMenuContent
73
+ className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg mb-4"
74
+ align="start"
75
+ side={isMobile ? "bottom" : "right"}
76
+ sideOffset={4}
77
+ >
78
+ <DropdownMenuLabel className="text-xs text-muted-foreground">
79
+ Teams
80
+ </DropdownMenuLabel>
81
+ {teams.map((team, index) => (
82
+ <DropdownMenuItem
83
+ key={team.name}
84
+ onClick={() => setActiveTeam(team)}
85
+ className="gap-2 p-2"
86
+ >
87
+ <div className="flex size-6 items-center justify-center rounded-sm border">
88
+ <team.logo className="size-4 shrink-0" />
89
+ </div>
90
+ {team.name}
91
+ <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>
92
+ </DropdownMenuItem>
93
+ ))}
94
+ <DropdownMenuSeparator />
95
+ <DropdownMenuItem className="gap-2 p-2">
96
+ <div className="flex size-6 items-center justify-center rounded-md border bg-background">
97
+ <Plus className="size-4" />
98
+ </div>
99
+ <div className="font-medium text-muted-foreground">Add team</div>
100
+ </DropdownMenuItem>
101
+ </DropdownMenuContent>
102
+ </DropdownMenu>
103
+ </SidebarMenuItem>
104
+ </SidebarMenu>
105
+ );
106
+ }