@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.
- package/package.json +86 -0
- package/src/client.tsx +31 -0
- package/src/components/admin/admin-user-menu.tsx +90 -0
- package/src/components/admin/developer-log.tsx +356 -0
- package/src/components/ai-provider/model-list-editor.tsx +147 -0
- package/src/components/ai-provider/provider-form.tsx +434 -0
- package/src/components/ai-provider/provider-icon.tsx +43 -0
- package/src/components/ai-provider/provider-logo.tsx +87 -0
- package/src/components/ai-provider/provider-table.tsx +258 -0
- package/src/components/auth/admin-login-form.tsx +214 -0
- package/src/components/auth/auth-brand-panel.tsx +60 -0
- package/src/components/auth/auth-header.tsx +38 -0
- package/src/components/auth/auth-layout.tsx +66 -0
- package/src/components/auth/auth-providers.tsx +34 -0
- package/src/components/auth/icon-input.tsx +46 -0
- package/src/components/auth/oauth-section.tsx +51 -0
- package/src/components/auth/password-input.tsx +67 -0
- package/src/components/common/page-header.tsx +42 -0
- package/src/components/common/placeholder-page.tsx +42 -0
- package/src/components/component/component-form-dialog.tsx +348 -0
- package/src/components/component/component-import-dialog.tsx +235 -0
- package/src/components/component/component-table.tsx +153 -0
- package/src/components/data-table/data-table-pagination.tsx +114 -0
- package/src/components/data-table/data-table.tsx +218 -0
- package/src/components/data-table/table-detail-layout.tsx +99 -0
- package/src/components/dependency/changelog-dialog.tsx +108 -0
- package/src/components/dependency/columns.tsx +232 -0
- package/src/components/dependency/dependency-table.tsx +368 -0
- package/src/components/dependency/edit-dialog.tsx +200 -0
- package/src/components/knowledge-public/bookmark-form-dialog.tsx +215 -0
- package/src/components/knowledge-public/compliance-form-dialog.tsx +308 -0
- package/src/components/knowledge-public/legal-form-dialog.tsx +315 -0
- package/src/components/layout/sidebar-02/app-sidebar.tsx +160 -0
- package/src/components/layout/sidebar-02/index.tsx +33 -0
- package/src/components/layout/sidebar-02/logo.tsx +40 -0
- package/src/components/layout/sidebar-02/nav-main.tsx +153 -0
- package/src/components/layout/sidebar-02/nav-notifications.tsx +78 -0
- package/src/components/layout/sidebar-02/team-switcher.tsx +106 -0
- package/src/components/magicui/animated-theme-toggler.tsx +272 -0
- package/src/components/magicui/rainbow-button.tsx +78 -0
- package/src/components/market/columns.tsx +151 -0
- package/src/components/market/market-app-form.tsx +237 -0
- package/src/components/market/market-app-list.tsx +156 -0
- package/src/components/monitoring/error-list.tsx +143 -0
- package/src/components/monitoring/health-cards.tsx +152 -0
- package/src/components/monitoring/metrics-stats.tsx +184 -0
- package/src/components/monitoring/request-log-table.tsx +179 -0
- package/src/components/providers/theme-toggle.tsx +59 -0
- package/src/components/settings/avatar/avatar-config.ts +46 -0
- package/src/components/ui/alert-dialog.tsx +216 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/avatar.tsx +129 -0
- package/src/components/ui/badge.tsx +66 -0
- package/src/components/ui/button.tsx +84 -0
- package/src/components/ui/card.tsx +120 -0
- package/src/components/ui/checkbox.tsx +50 -0
- package/src/components/ui/collapsible.tsx +50 -0
- package/src/components/ui/dialog.tsx +185 -0
- package/src/components/ui/dropdown-menu.tsx +286 -0
- package/src/components/ui/field.tsx +255 -0
- package/src/components/ui/input-group.tsx +171 -0
- package/src/components/ui/input.tsx +36 -0
- package/src/components/ui/label.tsx +41 -0
- package/src/components/ui/pagination.tsx +146 -0
- package/src/components/ui/select.tsx +209 -0
- package/src/components/ui/separator.tsx +45 -0
- package/src/components/ui/sheet.tsx +164 -0
- package/src/components/ui/sidebar.tsx +719 -0
- package/src/components/ui/skeleton.tsx +30 -0
- package/src/components/ui/sonner.tsx +37 -0
- package/src/components/ui/switch.tsx +48 -0
- package/src/components/ui/table.tsx +133 -0
- package/src/components/ui/textarea.tsx +35 -0
- package/src/components/ui/tooltip.tsx +74 -0
- package/src/components/user/columns.tsx +153 -0
- package/src/components/user/settings-activity-log.tsx +358 -0
- package/src/components/user/settings-profile/constants.ts +42 -0
- package/src/components/user/settings-profile/types.ts +47 -0
- package/src/components/user/settings-profile.tsx +664 -0
- package/src/components/user/user-create-dialog.tsx +137 -0
- package/src/components/user/user-filters.tsx +117 -0
- package/src/hooks/use-mobile.ts +36 -0
- package/src/instrument.client.ts +34 -0
- package/src/lib/anyhub/capabilities.ts +59 -0
- package/src/lib/anyhub/types.ts +43 -0
- package/src/lib/auth-client.ts +83 -0
- package/src/lib/auth-utils.ts +24 -0
- package/src/lib/auth.config.ts +264 -0
- package/src/lib/avatar.ts +41 -0
- package/src/lib/i18n/client.ts +32 -0
- package/src/lib/i18n/config.ts +40 -0
- package/src/lib/i18n/detect.ts +43 -0
- package/src/lib/i18n/locales/en/admin-ai.json +128 -0
- package/src/lib/i18n/locales/en/admin-billing.json +56 -0
- package/src/lib/i18n/locales/en/admin-users.json +88 -0
- package/src/lib/i18n/locales/en/common.json +877 -0
- package/src/lib/i18n/locales/en/enterprise.json +208 -0
- package/src/lib/i18n/locales/zh/admin-ai.json +128 -0
- package/src/lib/i18n/locales/zh/admin-billing.json +56 -0
- package/src/lib/i18n/locales/zh/admin-users.json +88 -0
- package/src/lib/i18n/locales/zh/common.json +877 -0
- package/src/lib/i18n/locales/zh/enterprise.json +208 -0
- package/src/lib/i18n/server.ts +18 -0
- package/src/lib/i18n/types.ts +5 -0
- package/src/lib/monitoring-benchmarks.ts +85 -0
- package/src/lib/monitoring-descriptions.ts +78 -0
- package/src/lib/prisma.ts +18 -0
- package/src/lib/router-context.ts +38 -0
- package/src/lib/session.ts +38 -0
- package/src/lib/utils.ts +23 -0
- package/src/providers/index.tsx +45 -0
- package/src/routeTree.gen.ts +1199 -0
- package/src/router.tsx +51 -0
- package/src/routes/__root.tsx +170 -0
- package/src/routes/_nav/ai-providers/$id/edit/route.lazy.tsx +74 -0
- package/src/routes/_nav/ai-providers/$id/edit/route.tsx +35 -0
- package/src/routes/_nav/ai-providers/index/route.lazy.tsx +33 -0
- package/src/routes/_nav/ai-providers/new/route.lazy.tsx +31 -0
- package/src/routes/_nav/ai-providers/new/route.tsx +20 -0
- package/src/routes/_nav/ai-providers/route.lazy.tsx +26 -0
- package/src/routes/_nav/ai-providers/route.tsx +31 -0
- package/src/routes/_nav/apps/route.lazy.tsx +30 -0
- package/src/routes/_nav/apps/route.tsx +20 -0
- package/src/routes/_nav/audit/route.lazy.tsx +100 -0
- package/src/routes/_nav/audit/route.tsx +25 -0
- package/src/routes/_nav/billing/invoices/route.lazy.tsx +86 -0
- package/src/routes/_nav/billing/invoices/route.tsx +25 -0
- package/src/routes/_nav/billing/route.lazy.tsx +48 -0
- package/src/routes/_nav/billing/route.tsx +27 -0
- package/src/routes/_nav/billing/subscriptions/$subscriptionId/route.lazy.tsx +160 -0
- package/src/routes/_nav/billing/subscriptions/$subscriptionId/route.tsx +25 -0
- package/src/routes/_nav/billing/subscriptions/route.lazy.tsx +113 -0
- package/src/routes/_nav/billing/subscriptions/route.tsx +28 -0
- package/src/routes/_nav/compliance/route.lazy.tsx +184 -0
- package/src/routes/_nav/compliance/route.tsx +25 -0
- package/src/routes/_nav/components/route.lazy.tsx +184 -0
- package/src/routes/_nav/components/route.tsx +40 -0
- package/src/routes/_nav/dashboard/route.lazy.tsx +33 -0
- package/src/routes/_nav/dashboard/route.tsx +20 -0
- package/src/routes/_nav/dependencies/route.lazy.tsx +66 -0
- package/src/routes/_nav/dependencies/route.tsx +32 -0
- package/src/routes/_nav/developer-logs/route.lazy.tsx +6 -0
- package/src/routes/_nav/developer-logs/route.tsx +14 -0
- package/src/routes/_nav/docs/route.lazy.tsx +30 -0
- package/src/routes/_nav/docs/route.tsx +20 -0
- package/src/routes/_nav/knowledge-public/bookmarks/route.lazy.tsx +355 -0
- package/src/routes/_nav/knowledge-public/bookmarks/route.tsx +40 -0
- package/src/routes/_nav/knowledge-public/compliance/route.lazy.tsx +245 -0
- package/src/routes/_nav/knowledge-public/compliance/route.tsx +40 -0
- package/src/routes/_nav/knowledge-public/legal/route.lazy.tsx +242 -0
- package/src/routes/_nav/knowledge-public/legal/route.tsx +40 -0
- package/src/routes/_nav/licenses/$licenseId/route.lazy.tsx +235 -0
- package/src/routes/_nav/licenses/$licenseId/route.tsx +25 -0
- package/src/routes/_nav/licenses/new/route.lazy.tsx +237 -0
- package/src/routes/_nav/licenses/route.lazy.tsx +143 -0
- package/src/routes/_nav/licenses/route.tsx +25 -0
- package/src/routes/_nav/market/route.lazy.tsx +31 -0
- package/src/routes/_nav/market/route.tsx +20 -0
- package/src/routes/_nav/monitoring/ai-usage/route.lazy.tsx +305 -0
- package/src/routes/_nav/monitoring/ai-usage/route.tsx +3 -0
- package/src/routes/_nav/monitoring/errors/route.lazy.tsx +111 -0
- package/src/routes/_nav/monitoring/health/route.lazy.tsx +260 -0
- package/src/routes/_nav/monitoring/logs/route.lazy.tsx +165 -0
- package/src/routes/_nav/monitoring/metrics/route.lazy.tsx +240 -0
- package/src/routes/_nav/monitoring/overview/route.lazy.tsx +138 -0
- package/src/routes/_nav/monitoring/route.lazy.tsx +68 -0
- package/src/routes/_nav/monitoring/route.tsx +20 -0
- package/src/routes/_nav/monitoring/sentry/route.lazy.tsx +399 -0
- package/src/routes/_nav/monitoring/sentry/route.tsx +20 -0
- package/src/routes/_nav/permissions/route.lazy.tsx +504 -0
- package/src/routes/_nav/permissions/route.tsx +26 -0
- package/src/routes/_nav/route.lazy.tsx +31 -0
- package/src/routes/_nav/route.tsx +34 -0
- package/src/routes/_nav/settings/route.lazy.tsx +30 -0
- package/src/routes/_nav/settings/route.tsx +20 -0
- package/src/routes/_nav/sso/route.lazy.tsx +145 -0
- package/src/routes/_nav/sso/route.tsx +25 -0
- package/src/routes/_nav/users/$userId/route.lazy.tsx +432 -0
- package/src/routes/_nav/users/$userId/route.tsx +27 -0
- package/src/routes/_nav/users/-components/users-table.tsx +38 -0
- package/src/routes/_nav/users/route.lazy.tsx +74 -0
- package/src/routes/_nav/users/route.tsx +43 -0
- package/src/routes/api/compliance-download/$jobId.ts +35 -0
- package/src/routes/api/observability/errors.ts +41 -0
- package/src/routes/api/observability/health.ts +30 -0
- package/src/routes/api.auth.$.ts +31 -0
- package/src/routes/api.sentry-test.ts +33 -0
- package/src/routes/index/route.lazy.tsx +22 -0
- package/src/routes/index/route.tsx +24 -0
- package/src/routes/login/route.lazy.tsx +46 -0
- package/src/routes/login/route.tsx +28 -0
- package/src/routes/sentry-tunnel.ts +61 -0
- package/src/routes/setup/route.lazy.tsx +94 -0
- package/src/routes/setup/route.tsx +28 -0
- package/src/server/__tests__/admin-ai-alerts.test.ts +113 -0
- package/src/server/__tests__/licenses-issue.test.ts +98 -0
- package/src/server/admin-ai-alerts.ts +177 -0
- package/src/server/admin-ai-usage.ts +134 -0
- package/src/server/auth-helpers.ts +51 -0
- package/src/server/entitlements.ts +24 -0
- package/src/server/knowledge-public/canonical-id.ts +55 -0
- package/src/server/knowledge-public/github-sync.ts +86 -0
- package/src/server/license-issue.ts +78 -0
- package/src/server/middlewares/request.ts +26 -0
- package/src/server/observability/debug-middleware.ts +39 -0
- package/src/server/observability/error-reporter.ts +99 -0
- package/src/server/observability/health-check.ts +140 -0
- package/src/server/observability/index.ts +33 -0
- package/src/server/observability/logger.ts +49 -0
- package/src/server/observability/metrics.ts +105 -0
- package/src/server/observability/request-middleware.ts +125 -0
- package/src/server/observability/timing.ts +42 -0
- package/src/server/pro-services.ts +32 -0
- package/src/server/serverFns/admin-billing.ts +94 -0
- package/src/server/serverFns/ai-provider/create.ts +92 -0
- package/src/server/serverFns/ai-provider/delete.ts +42 -0
- package/src/server/serverFns/ai-provider/get.ts +35 -0
- package/src/server/serverFns/ai-provider/list.ts +92 -0
- package/src/server/serverFns/ai-provider/update.ts +73 -0
- package/src/server/serverFns/anyhub/market.ts +64 -0
- package/src/server/serverFns/audit.ts +57 -0
- package/src/server/serverFns/auth/session.ts +26 -0
- package/src/server/serverFns/auth/setup.ts +73 -0
- package/src/server/serverFns/compliance.ts +85 -0
- package/src/server/serverFns/components/import.ts +197 -0
- package/src/server/serverFns/components/list.ts +51 -0
- package/src/server/serverFns/components/mutations.ts +84 -0
- package/src/server/serverFns/dependency/helpers.ts +245 -0
- package/src/server/serverFns/dependency/list.ts +127 -0
- package/src/server/serverFns/dependency/sync.ts +123 -0
- package/src/server/serverFns/dependency/update.ts +51 -0
- package/src/server/serverFns/developer-log-auto-generate.ts +176 -0
- package/src/server/serverFns/developer-logs.ts +132 -0
- package/src/server/serverFns/knowledge-public/bookmarks/create.ts +53 -0
- package/src/server/serverFns/knowledge-public/bookmarks/delete.ts +31 -0
- package/src/server/serverFns/knowledge-public/bookmarks/detail.ts +32 -0
- package/src/server/serverFns/knowledge-public/bookmarks/list.ts +66 -0
- package/src/server/serverFns/knowledge-public/bookmarks/sync-all-github.ts +56 -0
- package/src/server/serverFns/knowledge-public/bookmarks/update.ts +50 -0
- package/src/server/serverFns/knowledge-public/compliance/create.ts +58 -0
- package/src/server/serverFns/knowledge-public/compliance/delete.ts +31 -0
- package/src/server/serverFns/knowledge-public/compliance/detail.ts +32 -0
- package/src/server/serverFns/knowledge-public/compliance/list.ts +59 -0
- package/src/server/serverFns/knowledge-public/compliance/update.ts +53 -0
- package/src/server/serverFns/knowledge-public/legal/create.ts +62 -0
- package/src/server/serverFns/knowledge-public/legal/delete.ts +31 -0
- package/src/server/serverFns/knowledge-public/legal/detail.ts +32 -0
- package/src/server/serverFns/knowledge-public/legal/list.ts +60 -0
- package/src/server/serverFns/knowledge-public/legal/update.ts +55 -0
- package/src/server/serverFns/licenses.ts +92 -0
- package/src/server/serverFns/locale.ts +10 -0
- package/src/server/serverFns/monitoring/check-ai-alerts.ts +13 -0
- package/src/server/serverFns/monitoring/get-ai-usage-overview.ts +25 -0
- package/src/server/serverFns/monitoring/get-errors.ts +43 -0
- package/src/server/serverFns/monitoring/get-health-settings.ts +35 -0
- package/src/server/serverFns/monitoring/get-health.ts +55 -0
- package/src/server/serverFns/monitoring/get-metrics.ts +46 -0
- package/src/server/serverFns/monitoring/get-request-logs.ts +66 -0
- package/src/server/serverFns/monitoring/report-error.ts +55 -0
- package/src/server/serverFns/monitoring/reset.ts +70 -0
- package/src/server/serverFns/permissions.ts +214 -0
- package/src/server/serverFns/sentry/config.ts +88 -0
- package/src/server/serverFns/sso-config.ts +83 -0
- package/src/server/serverFns/user/create.ts +40 -0
- package/src/server/serverFns/user/delete.ts +29 -0
- package/src/server/serverFns/user/detail.ts +45 -0
- package/src/server/serverFns/user/list.ts +56 -0
- package/src/server/serverFns/user/reset-password.ts +28 -0
- package/src/server/serverFns/user/update-role.ts +26 -0
- package/src/server/serverFns/user/update-status.ts +26 -0
- package/src/server.ts +27 -0
- package/src/start.ts +85 -0
- package/src/styles/_keyframe-animations.scss +91 -0
- package/src/styles/_variables.scss +296 -0
- package/src/styles/globals.css +200 -0
- package/src/types/start-authjs.d.ts +27 -0
|
@@ -0,0 +1,184 @@
|
|
|
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 { createLazyFileRoute } from '@tanstack/react-router'
|
|
19
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
20
|
+
import { useServerFn } from '@tanstack/react-start'
|
|
21
|
+
import { useTranslation } from 'react-i18next'
|
|
22
|
+
import { toast } from 'sonner'
|
|
23
|
+
import type React from 'react'
|
|
24
|
+
import { listComplianceExportsFn, executeComplianceJobFn } from '@/server/serverFns/compliance'
|
|
25
|
+
import { Badge } from '@/components/ui/badge'
|
|
26
|
+
import { Button } from '@/components/ui/button'
|
|
27
|
+
import { Loader2 } from 'lucide-react'
|
|
28
|
+
|
|
29
|
+
export const Route = createLazyFileRoute('/_nav/compliance')({
|
|
30
|
+
pendingComponent: () => (
|
|
31
|
+
<div className="flex h-64 items-center justify-center">
|
|
32
|
+
<div className="size-8 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
|
33
|
+
</div>
|
|
34
|
+
),
|
|
35
|
+
component: CompliancePage,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const QUERY_KEY = ['compliance-exports']
|
|
39
|
+
|
|
40
|
+
function CompliancePage(): React.ReactElement {
|
|
41
|
+
const { t } = useTranslation('enterprise')
|
|
42
|
+
const queryClient = useQueryClient()
|
|
43
|
+
|
|
44
|
+
const { data } = useQuery({
|
|
45
|
+
queryKey: QUERY_KEY,
|
|
46
|
+
queryFn: () => listComplianceExportsFn({ data: { page: 1, pageSize: 20 } }),
|
|
47
|
+
staleTime: Infinity,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const items = data?.items ?? []
|
|
51
|
+
const total = data?.total ?? 0
|
|
52
|
+
|
|
53
|
+
const executeFn = useServerFn(executeComplianceJobFn)
|
|
54
|
+
|
|
55
|
+
const handleExecute = async (jobId: string) => {
|
|
56
|
+
try {
|
|
57
|
+
await executeFn({ data: { jobId } })
|
|
58
|
+
toast.success(t('compliance.export.toast.executed'))
|
|
59
|
+
await queryClient.invalidateQueries({ queryKey: QUERY_KEY })
|
|
60
|
+
} catch (err) {
|
|
61
|
+
toast.error(err instanceof Error ? err.message : t('compliance.export.toast.executeFailed'))
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const isExpired = (job: { status: string; expiresAt: string | Date | null }) =>
|
|
66
|
+
job.status === 'completed' && job.expiresAt != null && new Date(job.expiresAt) < new Date()
|
|
67
|
+
|
|
68
|
+
const STATUS_KEYS: Record<string, string> = {
|
|
69
|
+
pending: 'pending',
|
|
70
|
+
processing: 'processing',
|
|
71
|
+
completed: 'completed',
|
|
72
|
+
failed: 'failed',
|
|
73
|
+
expired: 'expired',
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const SCOPE_KEYS: Record<string, string> = {
|
|
77
|
+
user: 'user',
|
|
78
|
+
workspace: 'workspace',
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const TYPE_KEYS: Record<string, string> = {
|
|
82
|
+
export: 'export',
|
|
83
|
+
delete: 'delete',
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div className="space-y-4">
|
|
88
|
+
<div>
|
|
89
|
+
<h1 className="text-2xl font-bold">{t('compliance.export.title')}</h1>
|
|
90
|
+
<p className="text-sm text-muted-foreground mt-1">{t('compliance.export.subtitle', { count: total })}</p>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
{items.length === 0 ? (
|
|
94
|
+
<div className="rounded-lg border border-border p-8 text-center text-muted-foreground">
|
|
95
|
+
{t('compliance.export.empty')}
|
|
96
|
+
</div>
|
|
97
|
+
) : (
|
|
98
|
+
<div className="rounded-lg border border-border overflow-hidden">
|
|
99
|
+
<table className="w-full text-sm">
|
|
100
|
+
<thead>
|
|
101
|
+
<tr className="border-b border-border bg-muted/50">
|
|
102
|
+
<th className="p-3 text-left font-medium">{t('compliance.export.table.type')}</th>
|
|
103
|
+
<th className="p-3 text-left font-medium">{t('compliance.export.table.scope')}</th>
|
|
104
|
+
<th className="p-3 text-left font-medium">{t('compliance.export.table.target')}</th>
|
|
105
|
+
<th className="p-3 text-left font-medium">{t('compliance.export.table.status')}</th>
|
|
106
|
+
<th className="p-3 text-left font-medium">{t('compliance.export.table.requester')}</th>
|
|
107
|
+
<th className="p-3 text-left font-medium">{t('compliance.export.table.createdAt')}</th>
|
|
108
|
+
<th className="p-3 text-left font-medium">{t('compliance.export.table.error')}</th>
|
|
109
|
+
<th className="p-3 text-left font-medium">{t('compliance.export.table.actions')}</th>
|
|
110
|
+
</tr>
|
|
111
|
+
</thead>
|
|
112
|
+
<tbody>
|
|
113
|
+
{items.map((item) => (
|
|
114
|
+
<tr key={item.id} className="border-b border-border last:border-0">
|
|
115
|
+
<td className="p-3">
|
|
116
|
+
<Badge>{t('compliance.export.type.' + (TYPE_KEYS[item.type] ?? item.type))}</Badge>
|
|
117
|
+
</td>
|
|
118
|
+
<td className="p-3">
|
|
119
|
+
{t('compliance.export.scope.' + (SCOPE_KEYS[item.scope] ?? item.scope))}
|
|
120
|
+
</td>
|
|
121
|
+
<td className="p-3">{item.targetId.slice(0, 12)}</td>
|
|
122
|
+
<td className="p-3">
|
|
123
|
+
<span
|
|
124
|
+
className={`inline-flex rounded-md px-2 py-0.5 text-xs ${
|
|
125
|
+
item.status === 'completed'
|
|
126
|
+
? 'bg-green-500/10 text-green-600 dark:text-green-400'
|
|
127
|
+
: item.status === 'failed'
|
|
128
|
+
? 'bg-destructive/10 text-destructive'
|
|
129
|
+
: 'bg-muted text-muted-foreground'
|
|
130
|
+
}`}
|
|
131
|
+
>
|
|
132
|
+
{t('compliance.export.status.' + (STATUS_KEYS[item.status] ?? item.status))}
|
|
133
|
+
</span>
|
|
134
|
+
</td>
|
|
135
|
+
<td className="p-3">{item.requestedBy?.slice(0, 8) || '—'}</td>
|
|
136
|
+
<td className="p-3">
|
|
137
|
+
{new Date(item.createdAt).toLocaleDateString('zh-CN')}
|
|
138
|
+
</td>
|
|
139
|
+
<td className="p-3 text-muted-foreground truncate max-w-[150px]">
|
|
140
|
+
{item.error || '—'}
|
|
141
|
+
</td>
|
|
142
|
+
<td className="p-3">
|
|
143
|
+
<div className="flex gap-2">
|
|
144
|
+
{item.status === 'pending' && (
|
|
145
|
+
<Button
|
|
146
|
+
variant="outline"
|
|
147
|
+
size="sm"
|
|
148
|
+
onClick={() => handleExecute(item.id)}
|
|
149
|
+
>
|
|
150
|
+
{t('compliance.export.actions.execute')}
|
|
151
|
+
</Button>
|
|
152
|
+
)}
|
|
153
|
+
{item.status === 'processing' && (
|
|
154
|
+
<span className="inline-flex items-center gap-1 text-xs text-muted-foreground">
|
|
155
|
+
<Loader2 className="size-3 animate-spin" />
|
|
156
|
+
{t('compliance.export.actions.processing')}
|
|
157
|
+
</span>
|
|
158
|
+
)}
|
|
159
|
+
{item.status === 'completed' && !isExpired(item) && (
|
|
160
|
+
<a
|
|
161
|
+
href={`/api/compliance-download/${item.id}`}
|
|
162
|
+
download={`allblue-export-${item.id}.json`}
|
|
163
|
+
>
|
|
164
|
+
<Button variant="outline" size="sm" asChild={false}>
|
|
165
|
+
<span>{t('compliance.export.actions.download')}</span>
|
|
166
|
+
</Button>
|
|
167
|
+
</a>
|
|
168
|
+
)}
|
|
169
|
+
{isExpired(item) && (
|
|
170
|
+
<span className="inline-flex items-center text-xs text-muted-foreground">
|
|
171
|
+
{t('compliance.export.actions.expired')}
|
|
172
|
+
</span>
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
175
|
+
</td>
|
|
176
|
+
</tr>
|
|
177
|
+
))}
|
|
178
|
+
</tbody>
|
|
179
|
+
</table>
|
|
180
|
+
</div>
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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 { createFileRoute } from '@tanstack/react-router'
|
|
19
|
+
import { listComplianceExportsFn } from '@/server/serverFns/compliance'
|
|
20
|
+
|
|
21
|
+
export const Route = createFileRoute('/_nav/compliance')({
|
|
22
|
+
loader: async () => {
|
|
23
|
+
return listComplianceExportsFn({ data: { page: 1, pageSize: 20 } })
|
|
24
|
+
},
|
|
25
|
+
})
|
|
@@ -0,0 +1,184 @@
|
|
|
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 { useState, useEffect } from 'react'
|
|
19
|
+
import { createLazyFileRoute, useNavigate, useRouter } from '@tanstack/react-router'
|
|
20
|
+
import { BUILTIN_COMPONENTS, registerComponents, unregisterComponent } from '@allbluecn/web-app/prototype'
|
|
21
|
+
import { Plus, Search, Download } from 'lucide-react'
|
|
22
|
+
import { Button } from '@/components/ui/button'
|
|
23
|
+
import { Input } from '@/components/ui/input'
|
|
24
|
+
import {
|
|
25
|
+
AlertDialog,
|
|
26
|
+
AlertDialogAction,
|
|
27
|
+
AlertDialogCancel,
|
|
28
|
+
AlertDialogContent,
|
|
29
|
+
AlertDialogDescription,
|
|
30
|
+
AlertDialogFooter,
|
|
31
|
+
AlertDialogHeader,
|
|
32
|
+
AlertDialogTitle,
|
|
33
|
+
} from '@/components/ui/alert-dialog'
|
|
34
|
+
import { cn } from '@/lib/utils'
|
|
35
|
+
import { ComponentTable, type ComponentItem } from '@/components/component/component-table'
|
|
36
|
+
import { ComponentFormDialog } from '@/components/component/component-form-dialog'
|
|
37
|
+
import { ComponentImportDialog } from '@/components/component/component-import-dialog'
|
|
38
|
+
import { deleteComponentFn } from '@/server/serverFns/components/mutations'
|
|
39
|
+
import { toast } from '@/components/ui/sonner'
|
|
40
|
+
|
|
41
|
+
const CATEGORY_TABS = [
|
|
42
|
+
{ key: 'all', label: '全部' },
|
|
43
|
+
{ key: '基础组件', label: '基础组件' },
|
|
44
|
+
{ key: '内容区块', label: '内容区块' },
|
|
45
|
+
{ key: '页面模版', label: '页面模版' },
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
export const Route = createLazyFileRoute('/_nav/components')({
|
|
49
|
+
pendingComponent: () => (
|
|
50
|
+
<div className="flex h-64 items-center justify-center">
|
|
51
|
+
<div className="size-8 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
|
52
|
+
</div>
|
|
53
|
+
),
|
|
54
|
+
component: ComponentsPage,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
function ComponentsPage() {
|
|
58
|
+
const { items, total } = Route.useLoaderData()
|
|
59
|
+
const { category, search: urlSearch } = Route.useSearch()
|
|
60
|
+
const navigate = useNavigate({ from: '/components' })
|
|
61
|
+
const router = useRouter()
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
registerComponents(BUILTIN_COMPONENTS)
|
|
65
|
+
return () => { BUILTIN_COMPONENTS.forEach((c) => unregisterComponent(c.type)) }
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
const [searchInput, setSearchInput] = useState(urlSearch ?? '')
|
|
69
|
+
const [createOpen, setCreateOpen] = useState(false)
|
|
70
|
+
const [editing, setEditing] = useState<ComponentItem | null>(null)
|
|
71
|
+
const [deleting, setDeleting] = useState<ComponentItem | null>(null)
|
|
72
|
+
const [importOpen, setImportOpen] = useState(false)
|
|
73
|
+
|
|
74
|
+
function updateSearch(patch: { category?: string; search?: string }) {
|
|
75
|
+
navigate({ search: (prev) => ({ ...prev, ...patch }) })
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function handleSearchSubmit() {
|
|
79
|
+
const q = searchInput.trim()
|
|
80
|
+
updateSearch({ search: q || undefined })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function handleDelete() {
|
|
84
|
+
if (!deleting) return
|
|
85
|
+
try {
|
|
86
|
+
await deleteComponentFn({ data: { id: deleting.id } })
|
|
87
|
+
toast.success('组件已删除')
|
|
88
|
+
setDeleting(null)
|
|
89
|
+
router.invalidate()
|
|
90
|
+
} catch (e) {
|
|
91
|
+
toast.error(e instanceof Error ? e.message : '删除失败')
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<div className="space-y-4 p-6">
|
|
97
|
+
<div className="flex items-center justify-between">
|
|
98
|
+
<div>
|
|
99
|
+
<h2 className="text-xl font-bold">组件管理</h2>
|
|
100
|
+
<p className="text-sm text-muted-foreground">共 {total} 个组件</p>
|
|
101
|
+
</div>
|
|
102
|
+
<div className="flex items-center gap-2">
|
|
103
|
+
<Button variant="outline" onClick={() => setImportOpen(true)}>
|
|
104
|
+
<Download className="mr-1 size-4" />
|
|
105
|
+
导入组件
|
|
106
|
+
</Button>
|
|
107
|
+
<Button onClick={() => setCreateOpen(true)}>
|
|
108
|
+
<Plus className="mr-1 size-4" />
|
|
109
|
+
新建组件
|
|
110
|
+
</Button>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<div className="flex items-center gap-2">
|
|
115
|
+
<div className="flex items-center gap-1">
|
|
116
|
+
{CATEGORY_TABS.map((tab) => (
|
|
117
|
+
<button
|
|
118
|
+
key={tab.key}
|
|
119
|
+
onClick={() => updateSearch({ category: tab.key === 'all' ? undefined : tab.key })}
|
|
120
|
+
className={cn(
|
|
121
|
+
'rounded-md px-3 py-1.5 text-sm transition-colors',
|
|
122
|
+
(category ?? 'all') === tab.key
|
|
123
|
+
? 'bg-primary text-primary-foreground'
|
|
124
|
+
: 'text-muted-foreground hover:bg-muted',
|
|
125
|
+
)}
|
|
126
|
+
>
|
|
127
|
+
{tab.label}
|
|
128
|
+
</button>
|
|
129
|
+
))}
|
|
130
|
+
</div>
|
|
131
|
+
<div className="relative ml-auto w-64">
|
|
132
|
+
<Search className="absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
|
133
|
+
<Input
|
|
134
|
+
value={searchInput}
|
|
135
|
+
onChange={(e) => setSearchInput(e.target.value)}
|
|
136
|
+
onKeyDown={(e) => { if (e.key === 'Enter') handleSearchSubmit() }}
|
|
137
|
+
placeholder="搜索组件..."
|
|
138
|
+
className="pl-8"
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<ComponentTable
|
|
144
|
+
items={items as unknown as ComponentItem[]}
|
|
145
|
+
onEdit={(item) => setEditing(item)}
|
|
146
|
+
onDelete={(item) => setDeleting(item)}
|
|
147
|
+
/>
|
|
148
|
+
|
|
149
|
+
<ComponentFormDialog
|
|
150
|
+
open={createOpen}
|
|
151
|
+
onOpenChange={setCreateOpen}
|
|
152
|
+
component={null}
|
|
153
|
+
onSuccess={() => router.invalidate()}
|
|
154
|
+
/>
|
|
155
|
+
<ComponentFormDialog
|
|
156
|
+
open={!!editing}
|
|
157
|
+
onOpenChange={(o) => !o && setEditing(null)}
|
|
158
|
+
component={editing}
|
|
159
|
+
onSuccess={() => router.invalidate()}
|
|
160
|
+
/>
|
|
161
|
+
|
|
162
|
+
<ComponentImportDialog
|
|
163
|
+
open={importOpen}
|
|
164
|
+
onOpenChange={setImportOpen}
|
|
165
|
+
onSuccess={() => router.invalidate()}
|
|
166
|
+
/>
|
|
167
|
+
|
|
168
|
+
<AlertDialog open={!!deleting} onOpenChange={(o) => !o && setDeleting(null)}>
|
|
169
|
+
<AlertDialogContent>
|
|
170
|
+
<AlertDialogHeader>
|
|
171
|
+
<AlertDialogTitle>删除组件「{deleting?.name}」?</AlertDialogTitle>
|
|
172
|
+
<AlertDialogDescription>
|
|
173
|
+
组件将被标记为已删除(软删除),不影响已使用该组件的原型页面。可在数据库中恢复。
|
|
174
|
+
</AlertDialogDescription>
|
|
175
|
+
</AlertDialogHeader>
|
|
176
|
+
<AlertDialogFooter>
|
|
177
|
+
<AlertDialogCancel>取消</AlertDialogCancel>
|
|
178
|
+
<AlertDialogAction onClick={handleDelete}>删除</AlertDialogAction>
|
|
179
|
+
</AlertDialogFooter>
|
|
180
|
+
</AlertDialogContent>
|
|
181
|
+
</AlertDialog>
|
|
182
|
+
</div>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
@@ -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 { createFileRoute } from '@tanstack/react-router'
|
|
19
|
+
import { z } from 'zod'
|
|
20
|
+
import { listComponentsFn } from '@/server/serverFns/components/list'
|
|
21
|
+
|
|
22
|
+
const componentSearchSchema = z.object({
|
|
23
|
+
search: z.string().optional(),
|
|
24
|
+
category: z.string().optional(),
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export const Route = createFileRoute('/_nav/components')({
|
|
28
|
+
validateSearch: componentSearchSchema,
|
|
29
|
+
loaderDeps: ({ search }) => search,
|
|
30
|
+
loader: async ({ deps }) => {
|
|
31
|
+
return listComponentsFn({
|
|
32
|
+
data: {
|
|
33
|
+
page: 1,
|
|
34
|
+
pageSize: 50,
|
|
35
|
+
search: deps.search,
|
|
36
|
+
category: deps.category,
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
},
|
|
40
|
+
})
|
|
@@ -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 { createLazyFileRoute } from '@tanstack/react-router'
|
|
19
|
+
import { useTranslation } from 'react-i18next'
|
|
20
|
+
|
|
21
|
+
export const Route = createLazyFileRoute('/_nav/dashboard')({
|
|
22
|
+
component: DashboardPage,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
function DashboardPage() {
|
|
26
|
+
const { t } = useTranslation('common')
|
|
27
|
+
return (
|
|
28
|
+
<div className="p-6">
|
|
29
|
+
<h2 className="text-2xl font-bold">{t('dashboard.title')}</h2>
|
|
30
|
+
<p className="mt-2 text-muted-foreground">{t('dashboard.subtitle')}</p>
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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 { createFileRoute } from '@tanstack/react-router'
|
|
19
|
+
|
|
20
|
+
export const Route = createFileRoute('/_nav/dashboard')({})
|
|
@@ -0,0 +1,66 @@
|
|
|
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 { useState } from "react"
|
|
19
|
+
import { useTranslation } from 'react-i18next'
|
|
20
|
+
import { createLazyFileRoute } from "@tanstack/react-router"
|
|
21
|
+
import { Boxes, Info } from "lucide-react"
|
|
22
|
+
import type { ThirdPartyDependencyRow } from "@allbluecn/kernel"
|
|
23
|
+
import { listDependenciesFn } from "@/server/serverFns/dependency/list"
|
|
24
|
+
import { DependencyTable } from "@/components/dependency/dependency-table"
|
|
25
|
+
import { PageHeader } from "@/components/common/page-header"
|
|
26
|
+
|
|
27
|
+
export const Route = createLazyFileRoute("/_nav/dependencies")({
|
|
28
|
+
component: DependenciesPage,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
function DependenciesPage() {
|
|
32
|
+
const { t } = useTranslation('common')
|
|
33
|
+
const ctx = Route.useRouteContext() as { dependencies?: ThirdPartyDependencyRow[] }
|
|
34
|
+
const [data, setData] = useState<ThirdPartyDependencyRow[]>(ctx.dependencies ?? [])
|
|
35
|
+
|
|
36
|
+
async function refetch() {
|
|
37
|
+
try {
|
|
38
|
+
const fresh = await listDependenciesFn({ data: { watchedOnly: false } })
|
|
39
|
+
setData(fresh)
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.error("[dependencies] refetch 失败", e)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className="p-6 space-y-5">
|
|
47
|
+
<PageHeader
|
|
48
|
+
title={t('dependencies.title')}
|
|
49
|
+
description={t('dependencies.description')}
|
|
50
|
+
icon={Boxes}
|
|
51
|
+
/>
|
|
52
|
+
<div className="flex items-start gap-2 rounded-md border border-blue-500/20 bg-blue-500/5 p-3 text-xs text-muted-foreground">
|
|
53
|
+
<Info className="mt-0.5 size-3.5 shrink-0 text-blue-500" />
|
|
54
|
+
<div>
|
|
55
|
+
{t('dependencies.hint.currentVersion')}
|
|
56
|
+
<code className="font-mono">apps/web</code>、
|
|
57
|
+
<code className="font-mono">apps/admin</code>、
|
|
58
|
+
<code className="font-mono">packages/*</code> {t('dependencies.hint.currentVersionSuffix')}
|
|
59
|
+
{t('dependencies.hint.latestVersion')}
|
|
60
|
+
{t('dependencies.hint.watchList')}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<DependencyTable data={data} onMutated={refetch} />
|
|
64
|
+
</div>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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 { createFileRoute } from "@tanstack/react-router"
|
|
19
|
+
import { listDependenciesFn } from "@/server/serverFns/dependency/list"
|
|
20
|
+
import type { ThirdPartyDependencyRow } from "@allbluecn/kernel"
|
|
21
|
+
|
|
22
|
+
export const Route = createFileRoute("/_nav/dependencies")({
|
|
23
|
+
beforeLoad: async () => {
|
|
24
|
+
try {
|
|
25
|
+
const dependencies = await listDependenciesFn({ data: { watchedOnly: false } })
|
|
26
|
+
return { dependencies } satisfies { dependencies: ThirdPartyDependencyRow[] }
|
|
27
|
+
} catch (e) {
|
|
28
|
+
console.error("[admin/dependencies] 加载失败", e)
|
|
29
|
+
return { dependencies: [] as ThirdPartyDependencyRow[] }
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { listDeveloperLogsFn } from '@/server/serverFns/developer-logs'
|
|
3
|
+
import type { DeveloperLogEntry } from '@allbluecn/kernel'
|
|
4
|
+
|
|
5
|
+
export const Route = createFileRoute('/_nav/developer-logs')({
|
|
6
|
+
loader: async () => {
|
|
7
|
+
try {
|
|
8
|
+
const data = await listDeveloperLogsFn({ data: { page: 1, pageSize: 100 } })
|
|
9
|
+
return { entries: data.items, total: data.total }
|
|
10
|
+
} catch {
|
|
11
|
+
return { entries: [] as DeveloperLogEntry[], total: 0 }
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
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 { createLazyFileRoute } from '@tanstack/react-router'
|
|
19
|
+
import { useTranslation } from 'react-i18next'
|
|
20
|
+
import { FileText } from 'lucide-react'
|
|
21
|
+
import { PlaceholderPage } from '@/components/common/placeholder-page'
|
|
22
|
+
|
|
23
|
+
export const Route = createLazyFileRoute('/_nav/docs')({
|
|
24
|
+
component: DocsPage,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
function DocsPage() {
|
|
28
|
+
const { t } = useTranslation('common')
|
|
29
|
+
return <PlaceholderPage title={t('docs.title')} icon={FileText} />
|
|
30
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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 { createFileRoute } from '@tanstack/react-router'
|
|
19
|
+
|
|
20
|
+
export const Route = createFileRoute('/_nav/docs')({})
|