@allbluecn/admin-app 0.2.0 → 0.2.3
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 +10 -10
- package/src/components/admin/admin-user-menu.tsx +12 -6
- package/src/components/admin/developer-log.tsx +42 -44
- package/src/components/ai-provider/model-list-editor.tsx +5 -4
- package/src/components/ai-provider/provider-form.tsx +63 -64
- package/src/components/ai-provider/provider-table.tsx +35 -36
- package/src/components/auth/admin-login-form.tsx +23 -24
- package/src/components/auth/oauth-section.tsx +3 -5
- package/src/components/auth/password-input.tsx +2 -3
- package/src/components/common/placeholder-page.tsx +2 -4
- package/src/components/component/component-form-dialog.tsx +48 -49
- package/src/components/component/component-import-dialog.tsx +5 -5
- package/src/components/component/component-table.tsx +13 -14
- package/src/components/data-table/data-table-pagination.tsx +4 -6
- package/src/components/data-table/table-detail-layout.tsx +2 -3
- package/src/components/dependency/changelog-dialog.tsx +3 -4
- package/src/components/dependency/columns.tsx +32 -24
- package/src/components/dependency/dependency-table.tsx +37 -29
- package/src/components/dependency/edit-dialog.tsx +24 -25
- package/src/components/knowledge-public/bookmark-form-dialog.tsx +7 -7
- package/src/components/knowledge-public/compliance-form-dialog.tsx +13 -13
- package/src/components/knowledge-public/legal-form-dialog.tsx +51 -46
- package/src/components/layout/sidebar-02/app-sidebar.tsx +33 -34
- package/src/components/layout/sidebar-02/nav-main.tsx +1 -1
- package/src/components/market/columns.tsx +20 -21
- package/src/components/market/market-app-form.tsx +45 -38
- package/src/components/market/market-app-list.tsx +16 -17
- package/src/components/monitoring/error-list.tsx +18 -19
- package/src/components/monitoring/health-cards.tsx +11 -13
- package/src/components/monitoring/metrics-stats.tsx +11 -12
- package/src/components/monitoring/request-log-table.tsx +8 -9
- package/src/components/ui/pagination.tsx +0 -2
- package/src/components/ui/sidebar.tsx +1 -1
- package/src/components/user/columns.tsx +9 -9
- package/src/components/user/settings-activity-log.tsx +39 -29
- package/src/components/user/settings-profile.tsx +36 -36
- package/src/components/user/user-create-dialog.tsx +4 -4
- package/src/components/user/user-filters.tsx +16 -18
- package/src/lib/i18n/config.ts +0 -32
- package/src/providers/index.tsx +6 -16
- package/src/routeTree.gen.ts +1199 -0
- package/src/routes/__root.tsx +9 -23
- package/src/routes/_nav/apps/route.lazy.tsx +2 -3
- package/src/routes/_nav/audit/route.lazy.tsx +10 -11
- package/src/routes/_nav/billing/invoices/route.lazy.tsx +6 -7
- package/src/routes/_nav/billing/route.lazy.tsx +3 -4
- package/src/routes/_nav/billing/subscriptions/$subscriptionId/route.lazy.tsx +16 -17
- package/src/routes/_nav/billing/subscriptions/route.lazy.tsx +9 -10
- package/src/routes/_nav/compliance/route.lazy.tsx +34 -35
- package/src/routes/_nav/components/route.lazy.tsx +6 -6
- package/src/routes/_nav/dashboard/route.lazy.tsx +3 -5
- package/src/routes/_nav/dependencies/route.lazy.tsx +7 -8
- package/src/routes/_nav/docs/route.lazy.tsx +2 -3
- package/src/routes/_nav/knowledge-public/bookmarks/route.lazy.tsx +40 -41
- package/src/routes/_nav/knowledge-public/compliance/route.lazy.tsx +32 -33
- package/src/routes/_nav/knowledge-public/legal/route.lazy.tsx +32 -33
- package/src/routes/_nav/licenses/$licenseId/route.lazy.tsx +24 -31
- package/src/routes/_nav/licenses/new/route.lazy.tsx +25 -26
- package/src/routes/_nav/licenses/route.lazy.tsx +17 -18
- package/src/routes/_nav/monitoring/ai-usage/route.lazy.tsx +26 -27
- package/src/routes/_nav/monitoring/errors/route.lazy.tsx +11 -12
- package/src/routes/_nav/monitoring/health/route.lazy.tsx +18 -20
- package/src/routes/_nav/monitoring/logs/route.lazy.tsx +12 -13
- package/src/routes/_nav/monitoring/metrics/route.lazy.tsx +13 -15
- package/src/routes/_nav/monitoring/overview/route.lazy.tsx +12 -13
- package/src/routes/_nav/monitoring/route.lazy.tsx +8 -9
- package/src/routes/_nav/monitoring/sentry/route.lazy.tsx +55 -57
- package/src/routes/_nav/permissions/route.lazy.tsx +48 -50
- package/src/routes/_nav/settings/route.lazy.tsx +2 -3
- package/src/routes/_nav/sso/route.lazy.tsx +20 -18
- package/src/routes/_nav/users/$userId/route.lazy.tsx +86 -82
- package/src/routes/_nav/users/route.lazy.tsx +4 -5
- package/src/routes/setup/route.lazy.tsx +15 -16
- package/src/start.ts +2 -2
- package/src/lib/i18n/client.ts +0 -32
- package/src/lib/i18n/locales/en/admin-ai.json +0 -128
- package/src/lib/i18n/locales/en/admin-billing.json +0 -56
- package/src/lib/i18n/locales/en/admin-users.json +0 -88
- package/src/lib/i18n/locales/en/common.json +0 -877
- package/src/lib/i18n/locales/en/enterprise.json +0 -208
- package/src/lib/i18n/locales/zh/admin-ai.json +0 -128
- package/src/lib/i18n/locales/zh/admin-billing.json +0 -56
- package/src/lib/i18n/locales/zh/admin-users.json +0 -88
- package/src/lib/i18n/locales/zh/common.json +0 -877
- package/src/lib/i18n/locales/zh/enterprise.json +0 -208
- package/src/lib/i18n/server.ts +0 -18
- package/src/lib/i18n/types.ts +0 -5
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { createLazyFileRoute } from '@tanstack/react-router'
|
|
19
|
+
import { m } from '@/paraglide/messages.js'
|
|
19
20
|
import { useState, useEffect } from 'react'
|
|
20
|
-
import { useTranslation } from 'react-i18next'
|
|
21
21
|
import { Activity, RefreshCw } from 'lucide-react'
|
|
22
22
|
import { PageHeader } from '@/components/common/page-header'
|
|
23
23
|
import { HealthCards } from '@/components/monitoring/health-cards'
|
|
@@ -32,7 +32,6 @@ export const Route = createLazyFileRoute('/_nav/monitoring/overview')({
|
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
function MonitoringOverview() {
|
|
35
|
-
const { t } = useTranslation('common')
|
|
36
35
|
const [loading, setLoading] = useState(true)
|
|
37
36
|
const [health, setHealth] = useState<any>(null)
|
|
38
37
|
const [errors, setErrors] = useState<any>(null)
|
|
@@ -70,13 +69,13 @@ function MonitoringOverview() {
|
|
|
70
69
|
return (
|
|
71
70
|
<div className="space-y-4 my-4">
|
|
72
71
|
<div className="flex items-center justify-between">
|
|
73
|
-
<PageHeader title={
|
|
72
|
+
<PageHeader title={m.common_monitoring_overview_title()} description={m.common_monitoring_overview_description()} icon={Activity} />
|
|
74
73
|
<button
|
|
75
74
|
onClick={loadData}
|
|
76
75
|
className="inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium hover:bg-accent transition-colors"
|
|
77
76
|
>
|
|
78
77
|
<RefreshCw className="size-4" />
|
|
79
|
-
{
|
|
78
|
+
{m.common_common_refresh()}
|
|
80
79
|
</button>
|
|
81
80
|
</div>
|
|
82
81
|
|
|
@@ -85,23 +84,23 @@ function MonitoringOverview() {
|
|
|
85
84
|
<div className="grid gap-4 md:grid-cols-2">
|
|
86
85
|
<div className="rounded-lg border bg-card">
|
|
87
86
|
<div className="border-b px-4 py-3">
|
|
88
|
-
<h3 className="text-sm font-medium">{
|
|
87
|
+
<h3 className="text-sm font-medium">{m.common_monitoring_overview_requestStats_title()}</h3>
|
|
89
88
|
</div>
|
|
90
89
|
<div className="p-4 grid grid-cols-2 gap-3">
|
|
91
90
|
<div>
|
|
92
|
-
<p className="text-xs text-muted-foreground">{
|
|
91
|
+
<p className="text-xs text-muted-foreground">{m.common_monitoring_overview_requestStats_totalRequests()}</p>
|
|
93
92
|
<p className="text-lg font-semibold">{health?.requestStats?.total || 0}</p>
|
|
94
93
|
</div>
|
|
95
94
|
<div>
|
|
96
|
-
<p className="text-xs text-muted-foreground">{
|
|
95
|
+
<p className="text-xs text-muted-foreground">{m.common_monitoring_overview_requestStats_avgDuration()}</p>
|
|
97
96
|
<p className="text-lg font-semibold">{health?.requestStats?.avgDuration?.toFixed(1) || 0}ms</p>
|
|
98
97
|
</div>
|
|
99
98
|
<div>
|
|
100
|
-
<p className="text-xs text-muted-foreground">{
|
|
99
|
+
<p className="text-xs text-muted-foreground">{m.common_monitoring_overview_requestStats_maxDuration()}</p>
|
|
101
100
|
<p className="text-lg font-semibold">{health?.requestStats?.maxDuration || 0}ms</p>
|
|
102
101
|
</div>
|
|
103
102
|
<div>
|
|
104
|
-
<p className="text-xs text-muted-foreground">{
|
|
103
|
+
<p className="text-xs text-muted-foreground">{m.common_monitoring_overview_requestStats_errorTypes()}</p>
|
|
105
104
|
<p className="text-lg font-semibold">{health?.errorCount || 0}</p>
|
|
106
105
|
</div>
|
|
107
106
|
</div>
|
|
@@ -109,13 +108,13 @@ function MonitoringOverview() {
|
|
|
109
108
|
|
|
110
109
|
<div className="rounded-lg border bg-card">
|
|
111
110
|
<div className="border-b px-4 py-3">
|
|
112
|
-
<h3 className="text-sm font-medium">{
|
|
111
|
+
<h3 className="text-sm font-medium">{m.common_monitoring_overview_recentErrors_title()}</h3>
|
|
113
112
|
</div>
|
|
114
113
|
<div className="p-4">
|
|
115
114
|
{errors?.errors?.slice(0, 5).length > 0 ? (
|
|
116
115
|
<ErrorList errors={errors.errors.slice(0, 5)} />
|
|
117
116
|
) : (
|
|
118
|
-
<p className="text-sm text-muted-foreground text-center py-4">{
|
|
117
|
+
<p className="text-sm text-muted-foreground text-center py-4">{m.common_monitoring_overview_recentErrors_empty()}</p>
|
|
119
118
|
)}
|
|
120
119
|
</div>
|
|
121
120
|
</div>
|
|
@@ -123,13 +122,13 @@ function MonitoringOverview() {
|
|
|
123
122
|
|
|
124
123
|
<div className="rounded-lg border bg-card">
|
|
125
124
|
<div className="border-b px-4 py-3">
|
|
126
|
-
<h3 className="text-sm font-medium">{
|
|
125
|
+
<h3 className="text-sm font-medium">{m.common_monitoring_overview_metricsSnapshot_title()}</h3>
|
|
127
126
|
</div>
|
|
128
127
|
<div className="p-4">
|
|
129
128
|
{metrics?.current && Object.keys(metrics.current).length > 0 ? (
|
|
130
129
|
<MetricsStats data={metrics.current} />
|
|
131
130
|
) : (
|
|
132
|
-
<p className="text-sm text-muted-foreground text-center py-4">{
|
|
131
|
+
<p className="text-sm text-muted-foreground text-center py-4">{m.common_monitoring_overview_metricsSnapshot_empty()}</p>
|
|
133
132
|
)}
|
|
134
133
|
</div>
|
|
135
134
|
</div>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { createLazyFileRoute, Outlet, useLocation } from '@tanstack/react-router'
|
|
19
|
-
import {
|
|
19
|
+
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import { SidebarTrigger } from '@/components/ui/sidebar'
|
|
21
21
|
import { Separator } from '@/components/ui/separator'
|
|
22
22
|
|
|
@@ -25,17 +25,16 @@ export const Route = createLazyFileRoute('/_nav/monitoring')({
|
|
|
25
25
|
})
|
|
26
26
|
|
|
27
27
|
function MonitoringLayout() {
|
|
28
|
-
const { t } = useTranslation('common')
|
|
29
28
|
const location = useLocation()
|
|
30
29
|
|
|
31
30
|
const subNavItems = [
|
|
32
|
-
{ id: 'overview', label:
|
|
33
|
-
{ id: 'logs', label:
|
|
34
|
-
{ id: 'errors', label:
|
|
35
|
-
{ id: 'metrics', label:
|
|
36
|
-
{ id: 'health', label:
|
|
37
|
-
{ id: 'sentry', label:
|
|
38
|
-
{ id: 'ai-usage', label:
|
|
31
|
+
{ id: 'overview', label: m.common_monitoring_nav_overview(), path: '/monitoring/overview' },
|
|
32
|
+
{ id: 'logs', label: m.common_monitoring_nav_logs(), path: '/monitoring/logs' },
|
|
33
|
+
{ id: 'errors', label: m.common_monitoring_nav_errors(), path: '/monitoring/errors' },
|
|
34
|
+
{ id: 'metrics', label: m.common_monitoring_nav_metrics(), path: '/monitoring/metrics' },
|
|
35
|
+
{ id: 'health', label: m.common_monitoring_nav_health(), path: '/monitoring/health' },
|
|
36
|
+
{ id: 'sentry', label: m.common_monitoring_nav_sentry(), path: '/monitoring/sentry' },
|
|
37
|
+
{ id: 'ai-usage', label: m.common_monitoring_nav_aiUsage(), path: '/monitoring/ai-usage' },
|
|
39
38
|
]
|
|
40
39
|
|
|
41
40
|
const currentId = subNavItems.find((item) => location.pathname.startsWith(item.path))?.id || 'overview'
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { createLazyFileRoute, useRouter } from '@tanstack/react-router'
|
|
19
|
-
import {
|
|
19
|
+
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import * as Sentry from '@sentry/tanstackstart-react'
|
|
21
21
|
import { BugPlay, ExternalLink, Bug, Activity, AlertTriangle, Eye, EyeOff, Save, RotateCcw, CheckCircle2, XCircle, RefreshCw, ShieldCheck } from 'lucide-react'
|
|
22
22
|
import { IconBrandSentry } from '@tabler/icons-react';
|
|
@@ -38,7 +38,6 @@ type ConfigField = {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function SentryPage() {
|
|
41
|
-
const { t } = useTranslation('common')
|
|
42
41
|
const router = useRouter()
|
|
43
42
|
const [config, setConfig] = useState<SentryConfig>({ dsn: '', org: '', project: '', authToken: '' })
|
|
44
43
|
const [loading, setLoading] = useState(true)
|
|
@@ -55,32 +54,32 @@ function SentryPage() {
|
|
|
55
54
|
placeholder: 'https://<key>@o<orgId>.ingest.sentry.io/<projectId>',
|
|
56
55
|
secret: false,
|
|
57
56
|
instruction: [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
m.common_monitoring_sentry_fields_dsn_instruction_step1(),
|
|
58
|
+
m.common_monitoring_sentry_fields_dsn_instruction_step2(),
|
|
59
|
+
m.common_monitoring_sentry_fields_dsn_instruction_step3(),
|
|
60
|
+
m.common_monitoring_sentry_fields_dsn_instruction_step4(),
|
|
62
61
|
],
|
|
63
62
|
},
|
|
64
63
|
{
|
|
65
64
|
key: 'org',
|
|
66
|
-
label:
|
|
65
|
+
label: m.common_monitoring_sentry_fields_org_label(),
|
|
67
66
|
placeholder: 'my-organization',
|
|
68
67
|
secret: false,
|
|
69
68
|
instruction: [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
m.common_monitoring_sentry_fields_org_instruction_step1(),
|
|
70
|
+
m.common_monitoring_sentry_fields_org_instruction_step2(),
|
|
71
|
+
m.common_monitoring_sentry_fields_org_instruction_step3(),
|
|
73
72
|
],
|
|
74
73
|
},
|
|
75
74
|
{
|
|
76
75
|
key: 'project',
|
|
77
|
-
label:
|
|
76
|
+
label: m.common_monitoring_sentry_fields_project_label(),
|
|
78
77
|
placeholder: 'my-project',
|
|
79
78
|
secret: false,
|
|
80
79
|
instruction: [
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
m.common_monitoring_sentry_fields_project_instruction_step1(),
|
|
81
|
+
m.common_monitoring_sentry_fields_project_instruction_step2(),
|
|
82
|
+
m.common_monitoring_sentry_fields_project_instruction_step3(),
|
|
84
83
|
],
|
|
85
84
|
},
|
|
86
85
|
{
|
|
@@ -89,11 +88,11 @@ function SentryPage() {
|
|
|
89
88
|
placeholder: 'sntrys_********************',
|
|
90
89
|
secret: true,
|
|
91
90
|
instruction: [
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
m.common_monitoring_sentry_fields_authToken_instruction_step1(),
|
|
92
|
+
m.common_monitoring_sentry_fields_authToken_instruction_step2(),
|
|
93
|
+
m.common_monitoring_sentry_fields_authToken_instruction_step3(),
|
|
94
|
+
m.common_monitoring_sentry_fields_authToken_instruction_step4(),
|
|
95
|
+
m.common_monitoring_sentry_fields_authToken_instruction_step5(),
|
|
97
96
|
],
|
|
98
97
|
},
|
|
99
98
|
]
|
|
@@ -104,7 +103,7 @@ function SentryPage() {
|
|
|
104
103
|
const cfg = await getSentryConfig()
|
|
105
104
|
setConfig(cfg)
|
|
106
105
|
} catch {
|
|
107
|
-
toast.error(
|
|
106
|
+
toast.error(m.common_monitoring_sentry_loadFailed())
|
|
108
107
|
}
|
|
109
108
|
setLoading(false)
|
|
110
109
|
}
|
|
@@ -116,7 +115,7 @@ function SentryPage() {
|
|
|
116
115
|
const dsnConfigured = !!config.dsn || !!import.meta.env.VITE_SENTRY_DSN
|
|
117
116
|
|
|
118
117
|
const updateField = (key: keyof SentryConfig, value: string) => {
|
|
119
|
-
setConfig((prev) => ({ ...prev, [key]: value }))
|
|
118
|
+
setConfig((prev: any) => ({ ...prev, [key]: value }))
|
|
120
119
|
setChanged(true)
|
|
121
120
|
}
|
|
122
121
|
|
|
@@ -148,23 +147,23 @@ function SentryPage() {
|
|
|
148
147
|
await saveSentryConfig({ data: config })
|
|
149
148
|
reconfigure(config.dsn)
|
|
150
149
|
await reconfigureSentryServer({ data: { dsn: config.dsn } })
|
|
151
|
-
toast.success(
|
|
150
|
+
toast.success(m.common_monitoring_sentry_saveSuccess())
|
|
152
151
|
setChanged(false)
|
|
153
152
|
router.invalidate()
|
|
154
153
|
} catch {
|
|
155
|
-
toast.error(
|
|
154
|
+
toast.error(m.common_monitoring_sentry_saveFailed())
|
|
156
155
|
}
|
|
157
156
|
setSaving(false)
|
|
158
157
|
}
|
|
159
158
|
|
|
160
159
|
const validateDsn = (dsn: string) => {
|
|
161
|
-
if (!dsn) return { ok: false, detail:
|
|
162
|
-
if (!dsn.startsWith('https://')) return { ok: false, detail:
|
|
163
|
-
if (!dsn.includes('.sentry.io')) return { ok: false, detail:
|
|
164
|
-
if (!dsn.includes('@')) return { ok: false, detail:
|
|
160
|
+
if (!dsn) return { ok: false, detail: m.common_monitoring_sentry_validate_empty() }
|
|
161
|
+
if (!dsn.startsWith('https://')) return { ok: false, detail: m.common_monitoring_sentry_validate_httpsPrefix() }
|
|
162
|
+
if (!dsn.includes('.sentry.io')) return { ok: false, detail: m.common_monitoring_sentry_validate_notSentryIo() }
|
|
163
|
+
if (!dsn.includes('@')) return { ok: false, detail: m.common_monitoring_sentry_validate_missingAt() }
|
|
165
164
|
const projectId = dsn.split('/').pop()
|
|
166
|
-
if (!projectId || !/^\d+$/.test(projectId)) return { ok: false, detail:
|
|
167
|
-
return { ok: true, detail:
|
|
165
|
+
if (!projectId || !/^\d+$/.test(projectId)) return { ok: false, detail: m.common_monitoring_sentry_validate_missingProjectId() }
|
|
166
|
+
return { ok: true, detail: m.common_monitoring_sentry_validate_valid() }
|
|
168
167
|
}
|
|
169
168
|
|
|
170
169
|
const verifyConfig = async () => {
|
|
@@ -185,18 +184,18 @@ function SentryPage() {
|
|
|
185
184
|
|
|
186
185
|
const eventId = Sentry.captureException(new Error('Sentry config validation - test error'))
|
|
187
186
|
if (!eventId) {
|
|
188
|
-
setVerifyResult({ ok: false, detail:
|
|
187
|
+
setVerifyResult({ ok: false, detail: m.common_monitoring_sentry_verify_notInitialized() })
|
|
189
188
|
setVerifying(false)
|
|
190
189
|
return
|
|
191
190
|
}
|
|
192
191
|
const flushed = await Sentry.flush(5000)
|
|
193
192
|
if (flushed) {
|
|
194
|
-
setVerifyResult({ ok: true, detail:
|
|
193
|
+
setVerifyResult({ ok: true, detail: m.common_monitoring_sentry_verify_success({ eventId }) })
|
|
195
194
|
} else {
|
|
196
|
-
setVerifyResult({ ok: false, detail:
|
|
195
|
+
setVerifyResult({ ok: false, detail: m.common_monitoring_sentry_verify_timeout() })
|
|
197
196
|
}
|
|
198
197
|
} catch (e) {
|
|
199
|
-
setVerifyResult({ ok: false, detail:
|
|
198
|
+
setVerifyResult({ ok: false, detail: m.common_monitoring_sentry_verify_error({ message: (e as Error).message }) })
|
|
200
199
|
}
|
|
201
200
|
setVerifying(false)
|
|
202
201
|
}
|
|
@@ -207,9 +206,9 @@ function SentryPage() {
|
|
|
207
206
|
<div>
|
|
208
207
|
<h2 className="text-2xl font-bold flex items-center gap-2">
|
|
209
208
|
<IconBrandSentry stroke={2} />
|
|
210
|
-
{
|
|
209
|
+
{m.common_monitoring_sentry_title()}
|
|
211
210
|
</h2>
|
|
212
|
-
<p className="mt-1 text-muted-foreground">{
|
|
211
|
+
<p className="mt-1 text-muted-foreground">{m.common_monitoring_sentry_subtitle()}</p>
|
|
213
212
|
</div>
|
|
214
213
|
<a
|
|
215
214
|
href="https://sentry.io"
|
|
@@ -218,53 +217,53 @@ function SentryPage() {
|
|
|
218
217
|
className="inline-flex items-center gap-1.5 rounded-lg border bg-card px-4 py-2 text-sm font-medium hover:bg-accent transition-colors"
|
|
219
218
|
>
|
|
220
219
|
<ExternalLink className="size-4" />
|
|
221
|
-
{
|
|
220
|
+
{m.common_monitoring_sentry_openSentry()}
|
|
222
221
|
</a>
|
|
223
222
|
</div>
|
|
224
223
|
|
|
225
224
|
<div className="grid gap-4 md:grid-cols-4">
|
|
226
225
|
<StatusCard
|
|
227
|
-
title={
|
|
226
|
+
title={m.common_monitoring_sentry_statusCards_dsn_title()}
|
|
228
227
|
status={dsnConfigured ? 'active' : 'inactive'}
|
|
229
228
|
icon={dsnConfigured ? CheckCircle2 : XCircle}
|
|
230
|
-
description={dsnConfigured ?
|
|
229
|
+
description={dsnConfigured ? m.common_monitoring_sentry_statusCards_dsn_configured() : m.common_monitoring_sentry_statusCards_dsn_notConfigured()}
|
|
231
230
|
/>
|
|
232
231
|
<StatusCard
|
|
233
|
-
title={
|
|
232
|
+
title={m.common_monitoring_sentry_statusCards_errors_title()}
|
|
234
233
|
status={dsnConfigured ? 'active' : 'inactive'}
|
|
235
234
|
icon={Bug}
|
|
236
|
-
description={dsnConfigured ?
|
|
235
|
+
description={dsnConfigured ? m.common_monitoring_sentry_statusCards_errors_enabled() : m.common_monitoring_sentry_statusCards_errors_disabled()}
|
|
237
236
|
/>
|
|
238
237
|
<StatusCard
|
|
239
|
-
title={
|
|
238
|
+
title={m.common_monitoring_sentry_statusCards_performance_title()}
|
|
240
239
|
status={dsnConfigured ? 'active' : 'inactive'}
|
|
241
240
|
icon={Activity}
|
|
242
|
-
description={dsnConfigured ?
|
|
241
|
+
description={dsnConfigured ? m.common_monitoring_sentry_statusCards_performance_enabled() : m.common_monitoring_sentry_statusCards_performance_disabled()}
|
|
243
242
|
/>
|
|
244
243
|
<StatusCard
|
|
245
244
|
title="Session Replay"
|
|
246
245
|
status={dsnConfigured ? 'active' : 'inactive'}
|
|
247
246
|
icon={BugPlay}
|
|
248
|
-
description={dsnConfigured ?
|
|
247
|
+
description={dsnConfigured ? m.common_monitoring_sentry_statusCards_replay_enabled() : m.common_monitoring_sentry_statusCards_replay_disabled()}
|
|
249
248
|
/>
|
|
250
249
|
</div>
|
|
251
250
|
|
|
252
251
|
<div className="rounded-lg border bg-card">
|
|
253
252
|
<div className="border-b px-6 py-4">
|
|
254
|
-
<h3 className="text-lg font-semibold">{
|
|
253
|
+
<h3 className="text-lg font-semibold">{m.common_monitoring_sentry_config_title()}</h3>
|
|
255
254
|
<p className="text-sm text-muted-foreground mt-1">
|
|
256
|
-
{
|
|
255
|
+
{m.common_monitoring_sentry_config_description()}
|
|
257
256
|
</p>
|
|
258
257
|
</div>
|
|
259
258
|
<div className="divide-y">
|
|
260
259
|
{CONFIG_FIELDS.map((field) => (
|
|
261
260
|
<ConfigFieldCard
|
|
262
|
-
key={field.key}
|
|
261
|
+
key={String(field.key)}
|
|
263
262
|
field={field}
|
|
264
263
|
value={config[field.key]}
|
|
265
|
-
visible={visibleFields.has(field.key)}
|
|
266
|
-
onChange={(v) => updateField(field.key, v)}
|
|
267
|
-
onToggleVisible={() => toggleVisible(field.key)}
|
|
264
|
+
visible={visibleFields.has(String(field.key))}
|
|
265
|
+
onChange={(v: string) => updateField(field.key as any, v)}
|
|
266
|
+
onToggleVisible={() => toggleVisible(String(field.key))}
|
|
268
267
|
/>
|
|
269
268
|
))}
|
|
270
269
|
</div>
|
|
@@ -276,10 +275,10 @@ function SentryPage() {
|
|
|
276
275
|
className="inline-flex items-center gap-1.5 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:opacity-90 transition-opacity disabled:opacity-50"
|
|
277
276
|
>
|
|
278
277
|
{saving ? <RefreshCw className="size-4 animate-spin" /> : <Save className="size-4" />}
|
|
279
|
-
{
|
|
278
|
+
{m.common_monitoring_sentry_saveConfig()}
|
|
280
279
|
</button>
|
|
281
280
|
{changed && (
|
|
282
|
-
<span className="text-xs text-muted-foreground">{
|
|
281
|
+
<span className="text-xs text-muted-foreground">{m.common_monitoring_sentry_unsaved()}</span>
|
|
283
282
|
)}
|
|
284
283
|
</div>
|
|
285
284
|
</div>
|
|
@@ -299,12 +298,12 @@ function SentryPage() {
|
|
|
299
298
|
<div className="rounded-lg border bg-card p-6">
|
|
300
299
|
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
|
|
301
300
|
<ShieldCheck className="size-5" />
|
|
302
|
-
{
|
|
301
|
+
{m.common_monitoring_sentry_verify_title()}
|
|
303
302
|
</h3>
|
|
304
303
|
<p className="text-sm text-muted-foreground mb-4">
|
|
305
|
-
{
|
|
304
|
+
{m.common_monitoring_sentry_verify_description()}
|
|
306
305
|
<h4 className="text-xs text-muted-foreground mb-4">
|
|
307
|
-
{
|
|
306
|
+
{m.common_monitoring_sentry_verify_note()}
|
|
308
307
|
</h4>
|
|
309
308
|
</p>
|
|
310
309
|
<button
|
|
@@ -314,7 +313,7 @@ function SentryPage() {
|
|
|
314
313
|
className="inline-flex items-center gap-1.5 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:opacity-90 transition-opacity disabled:opacity-40"
|
|
315
314
|
>
|
|
316
315
|
{verifying ? <RefreshCw className="size-4 animate-spin" /> : <ShieldCheck className="size-4" />}
|
|
317
|
-
{
|
|
316
|
+
{m.common_monitoring_sentry_verify_button()}
|
|
318
317
|
</button>
|
|
319
318
|
</div>
|
|
320
319
|
</div>
|
|
@@ -334,7 +333,6 @@ function ConfigFieldCard({
|
|
|
334
333
|
onChange: (v: string) => void
|
|
335
334
|
onToggleVisible: () => void
|
|
336
335
|
}) {
|
|
337
|
-
const { t } = useTranslation('common')
|
|
338
336
|
return (
|
|
339
337
|
<div className="px-6 py-5 space-y-3">
|
|
340
338
|
<div className="flex items-center justify-between">
|
|
@@ -361,7 +359,7 @@ function ConfigFieldCard({
|
|
|
361
359
|
</div>
|
|
362
360
|
|
|
363
361
|
<div className="rounded-lg bg-muted/50 p-3">
|
|
364
|
-
<p className="text-xs font-medium text-muted-foreground mb-1.5">{
|
|
362
|
+
<p className="text-xs font-medium text-muted-foreground mb-1.5">{m.common_monitoring_sentry_fields_obtainMethod()}</p>
|
|
365
363
|
<ol className="list-decimal list-inside space-y-0.5">
|
|
366
364
|
{field.instruction.map((step, i) => (
|
|
367
365
|
<li key={i} className="text-xs text-muted-foreground">{step}</li>
|