@allbluecn/admin-app 0.1.0 → 0.2.1
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 +9 -9
- 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/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
|
@@ -80,7 +80,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
80
80
|
if (item) {
|
|
81
81
|
setLoading(true)
|
|
82
82
|
adminGetComplianceDetailFn({ data: { id: item.id } })
|
|
83
|
-
.then((d) => {
|
|
83
|
+
.then((d: any) => {
|
|
84
84
|
setForm({
|
|
85
85
|
title: d.title ?? '',
|
|
86
86
|
code: '',
|
|
@@ -98,7 +98,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
98
98
|
: [],
|
|
99
99
|
)
|
|
100
100
|
})
|
|
101
|
-
.catch((e) => toast.error(e instanceof Error ? e.message : '加载失败'))
|
|
101
|
+
.catch((e: unknown) => toast.error(e instanceof Error ? e.message : '加载失败'))
|
|
102
102
|
.finally(() => setLoading(false))
|
|
103
103
|
} else {
|
|
104
104
|
setForm(EMPTY)
|
|
@@ -173,7 +173,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
173
173
|
<Label>标题 *</Label>
|
|
174
174
|
<Input
|
|
175
175
|
value={form.title}
|
|
176
|
-
onChange={(e) => setForm((f) => ({ ...f, title: e.target.value }))}
|
|
176
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, title: e.target.value }))}
|
|
177
177
|
placeholder="如:支付卡行业数据安全标准"
|
|
178
178
|
/>
|
|
179
179
|
</div>
|
|
@@ -182,7 +182,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
182
182
|
<Label>发文字号</Label>
|
|
183
183
|
<Input
|
|
184
184
|
value={form.code}
|
|
185
|
-
onChange={(e) => setForm((f) => ({ ...f, code: e.target.value }))}
|
|
185
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, code: e.target.value }))}
|
|
186
186
|
placeholder="如:PCI DSS 4.0"
|
|
187
187
|
/>
|
|
188
188
|
</div>
|
|
@@ -190,7 +190,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
190
190
|
<Label>所属行业 *</Label>
|
|
191
191
|
<Input
|
|
192
192
|
value={form.industry}
|
|
193
|
-
onChange={(e) => setForm((f) => ({ ...f, industry: e.target.value }))}
|
|
193
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, industry: e.target.value }))}
|
|
194
194
|
placeholder="如:金融"
|
|
195
195
|
/>
|
|
196
196
|
</div>
|
|
@@ -198,7 +198,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
198
198
|
<Label>地区</Label>
|
|
199
199
|
<Input
|
|
200
200
|
value={form.region}
|
|
201
|
-
onChange={(e) => setForm((f) => ({ ...f, region: e.target.value }))}
|
|
201
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, region: e.target.value }))}
|
|
202
202
|
placeholder="如:全球"
|
|
203
203
|
/>
|
|
204
204
|
</div>
|
|
@@ -208,7 +208,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
208
208
|
<Label>监管机构</Label>
|
|
209
209
|
<Input
|
|
210
210
|
value={form.regulatoryBody}
|
|
211
|
-
onChange={(e) => setForm((f) => ({ ...f, regulatoryBody: e.target.value }))}
|
|
211
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, regulatoryBody: e.target.value }))}
|
|
212
212
|
placeholder="如:PCI SSC"
|
|
213
213
|
/>
|
|
214
214
|
</div>
|
|
@@ -216,7 +216,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
216
216
|
<Label>来源链接</Label>
|
|
217
217
|
<Input
|
|
218
218
|
value={form.sourceUrl}
|
|
219
|
-
onChange={(e) => setForm((f) => ({ ...f, sourceUrl: e.target.value }))}
|
|
219
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, sourceUrl: e.target.value }))}
|
|
220
220
|
placeholder="https://"
|
|
221
221
|
/>
|
|
222
222
|
</div>
|
|
@@ -225,7 +225,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
225
225
|
<Label>摘要</Label>
|
|
226
226
|
<Textarea
|
|
227
227
|
value={form.summary}
|
|
228
|
-
onChange={(e) => setForm((f) => ({ ...f, summary: e.target.value }))}
|
|
228
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setForm((f) => ({ ...f, summary: e.target.value }))}
|
|
229
229
|
rows={2}
|
|
230
230
|
placeholder="一句话简介"
|
|
231
231
|
/>
|
|
@@ -248,12 +248,12 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
248
248
|
<div className="grid flex-1 gap-1.5">
|
|
249
249
|
<Input
|
|
250
250
|
value={r.title}
|
|
251
|
-
onChange={(e) => updateReq(i, 'title', e.target.value)}
|
|
251
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => updateReq(i, 'title', e.target.value)}
|
|
252
252
|
placeholder="要求标题"
|
|
253
253
|
/>
|
|
254
254
|
<Textarea
|
|
255
255
|
value={r.detail}
|
|
256
|
-
onChange={(e) => updateReq(i, 'detail', e.target.value)}
|
|
256
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => updateReq(i, 'detail', e.target.value)}
|
|
257
257
|
rows={2}
|
|
258
258
|
placeholder="具体说明"
|
|
259
259
|
/>
|
|
@@ -277,7 +277,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
277
277
|
<Label>正文内容</Label>
|
|
278
278
|
<Textarea
|
|
279
279
|
value={form.content}
|
|
280
|
-
onChange={(e) => setForm((f) => ({ ...f, content: e.target.value }))}
|
|
280
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setForm((f) => ({ ...f, content: e.target.value }))}
|
|
281
281
|
rows={6}
|
|
282
282
|
placeholder="补充说明内容"
|
|
283
283
|
className="font-mono text-sm"
|
|
@@ -287,7 +287,7 @@ export function ComplianceFormDialog({ open, onOpenChange, item, onSuccess }: Co
|
|
|
287
287
|
<Label>标签(逗号分隔)</Label>
|
|
288
288
|
<Input
|
|
289
289
|
value={form.tags}
|
|
290
|
-
onChange={(e) => setForm((f) => ({ ...f, tags: e.target.value }))}
|
|
290
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, tags: e.target.value }))}
|
|
291
291
|
placeholder="如:金融, 安全"
|
|
292
292
|
/>
|
|
293
293
|
</div>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { useState, useEffect } from 'react'
|
|
19
|
-
import {
|
|
19
|
+
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import { Loader2 } from 'lucide-react'
|
|
21
21
|
import { toast } from '@/components/ui/sonner'
|
|
22
22
|
import { Button } from '@/components/ui/button'
|
|
@@ -65,6 +65,12 @@ const JURISDICTIONS: Array<{ value: 'CN_NATIONAL' | 'CN_LOCAL' | 'OVERSEAS' }> =
|
|
|
65
65
|
{ value: 'OVERSEAS' },
|
|
66
66
|
]
|
|
67
67
|
|
|
68
|
+
const JURISDICTION_LABELS: Record<string, () => string> = {
|
|
69
|
+
CN_NATIONAL: m.common_legalForm_jurisdictions_CN_NATIONAL,
|
|
70
|
+
CN_LOCAL: m.common_legalForm_jurisdictions_CN_LOCAL,
|
|
71
|
+
OVERSEAS: m.common_legalForm_jurisdictions_OVERSEAS,
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
interface LegalFormDialogProps {
|
|
69
75
|
open: boolean
|
|
70
76
|
onOpenChange: (open: boolean) => void
|
|
@@ -90,7 +96,6 @@ const EMPTY = {
|
|
|
90
96
|
const JURISDICTION_KEYS = ['CN_NATIONAL', 'CN_LOCAL', 'OVERSEAS'] as const
|
|
91
97
|
|
|
92
98
|
export function LegalFormDialog({ open, onOpenChange, item, onSuccess }: LegalFormDialogProps) {
|
|
93
|
-
const { t } = useTranslation('common')
|
|
94
99
|
const isEdit = !!item
|
|
95
100
|
const [form, setForm] = useState(EMPTY)
|
|
96
101
|
const [saving, setSaving] = useState(false)
|
|
@@ -101,7 +106,7 @@ export function LegalFormDialog({ open, onOpenChange, item, onSuccess }: LegalFo
|
|
|
101
106
|
if (item) {
|
|
102
107
|
setLoading(true)
|
|
103
108
|
adminGetLegalDetailFn({ data: { id: item.id } })
|
|
104
|
-
.then((d) => {
|
|
109
|
+
.then((d: any) => {
|
|
105
110
|
setForm({
|
|
106
111
|
title: d.title ?? '',
|
|
107
112
|
code: '',
|
|
@@ -119,7 +124,7 @@ export function LegalFormDialog({ open, onOpenChange, item, onSuccess }: LegalFo
|
|
|
119
124
|
tags: ((d.tags as string[]) ?? []).join(', '),
|
|
120
125
|
})
|
|
121
126
|
})
|
|
122
|
-
.catch((e) => toast.error(e instanceof Error ? e.message :
|
|
127
|
+
.catch((e: unknown) => toast.error(e instanceof Error ? e.message : m.common_legalForm_loadFailed()))
|
|
123
128
|
.finally(() => setLoading(false))
|
|
124
129
|
} else {
|
|
125
130
|
setForm(EMPTY)
|
|
@@ -129,11 +134,11 @@ export function LegalFormDialog({ open, onOpenChange, item, onSuccess }: LegalFo
|
|
|
129
134
|
async function handleSubmit(e: React.FormEvent) {
|
|
130
135
|
e.preventDefault()
|
|
131
136
|
if (!form.title.trim()) {
|
|
132
|
-
toast.error(
|
|
137
|
+
toast.error(m.common_legalForm_titleRequired())
|
|
133
138
|
return
|
|
134
139
|
}
|
|
135
140
|
if (!form.category.trim()) {
|
|
136
|
-
toast.error(
|
|
141
|
+
toast.error(m.common_legalForm_categoryRequired())
|
|
137
142
|
return
|
|
138
143
|
}
|
|
139
144
|
setSaving(true)
|
|
@@ -154,15 +159,15 @@ export function LegalFormDialog({ open, onOpenChange, item, onSuccess }: LegalFo
|
|
|
154
159
|
}
|
|
155
160
|
if (isEdit && item) {
|
|
156
161
|
await adminUpdateLegalFn({ data: { id: item.id, ...payload } })
|
|
157
|
-
toast.success(
|
|
162
|
+
toast.success(m.common_legalForm_updated())
|
|
158
163
|
} else {
|
|
159
164
|
await adminCreateLegalFn({ data: payload })
|
|
160
|
-
toast.success(
|
|
165
|
+
toast.success(m.common_legalForm_created())
|
|
161
166
|
}
|
|
162
167
|
onSuccess()
|
|
163
168
|
onOpenChange(false)
|
|
164
169
|
} catch (err) {
|
|
165
|
-
toast.error(err instanceof Error ? err.message :
|
|
170
|
+
toast.error(err instanceof Error ? err.message : m.common_legalForm_saveFailed())
|
|
166
171
|
} finally {
|
|
167
172
|
setSaving(false)
|
|
168
173
|
}
|
|
@@ -172,7 +177,7 @@ export function LegalFormDialog({ open, onOpenChange, item, onSuccess }: LegalFo
|
|
|
172
177
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
173
178
|
<DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-2xl">
|
|
174
179
|
<DialogHeader>
|
|
175
|
-
<DialogTitle>{isEdit ?
|
|
180
|
+
<DialogTitle>{isEdit ? m.common_legalForm_edit() : m.common_legalForm_create()}</DialogTitle>
|
|
176
181
|
</DialogHeader>
|
|
177
182
|
{loading ? (
|
|
178
183
|
<div className="flex h-40 items-center justify-center">
|
|
@@ -182,27 +187,27 @@ export function LegalFormDialog({ open, onOpenChange, item, onSuccess }: LegalFo
|
|
|
182
187
|
<form onSubmit={handleSubmit}>
|
|
183
188
|
<div className="grid gap-4 py-1">
|
|
184
189
|
<div className="grid gap-2">
|
|
185
|
-
<Label>{
|
|
190
|
+
<Label>{m.common_legalForm_title()}</Label>
|
|
186
191
|
<Input
|
|
187
192
|
value={form.title}
|
|
188
|
-
onChange={(e) => setForm((f) => ({ ...f, title: e.target.value }))}
|
|
189
|
-
placeholder={
|
|
193
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, title: e.target.value }))}
|
|
194
|
+
placeholder={m.common_legalForm_titlePlaceholder()}
|
|
190
195
|
/>
|
|
191
196
|
</div>
|
|
192
197
|
<div className="grid grid-cols-2 gap-4">
|
|
193
198
|
<div className="grid gap-2">
|
|
194
|
-
<Label>{
|
|
199
|
+
<Label>{m.common_legalForm_code()}</Label>
|
|
195
200
|
<Input
|
|
196
201
|
value={form.code}
|
|
197
|
-
onChange={(e) => setForm((f) => ({ ...f, code: e.target.value }))}
|
|
198
|
-
placeholder={
|
|
202
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, code: e.target.value }))}
|
|
203
|
+
placeholder={m.common_legalForm_codePlaceholder()}
|
|
199
204
|
/>
|
|
200
205
|
</div>
|
|
201
206
|
<div className="grid gap-2">
|
|
202
|
-
<Label>{
|
|
207
|
+
<Label>{m.common_legalForm_jurisdiction()}</Label>
|
|
203
208
|
<Select
|
|
204
209
|
value={form.jurisdiction}
|
|
205
|
-
onValueChange={(v) => setForm((f) => ({ ...f, jurisdiction: v }))}
|
|
210
|
+
onValueChange={(v: string) => setForm((f) => ({ ...f, jurisdiction: v }))}
|
|
206
211
|
>
|
|
207
212
|
<SelectTrigger className="w-full">
|
|
208
213
|
<SelectValue />
|
|
@@ -210,7 +215,7 @@ export function LegalFormDialog({ open, onOpenChange, item, onSuccess }: LegalFo
|
|
|
210
215
|
<SelectContent>
|
|
211
216
|
{JURISDICTIONS.map((j) => (
|
|
212
217
|
<SelectItem key={j.value} value={j.value}>
|
|
213
|
-
{
|
|
218
|
+
{(JURISDICTION_LABELS[j.value] ?? (() => j.value))()}
|
|
214
219
|
</SelectItem>
|
|
215
220
|
))}
|
|
216
221
|
</SelectContent>
|
|
@@ -219,92 +224,92 @@ export function LegalFormDialog({ open, onOpenChange, item, onSuccess }: LegalFo
|
|
|
219
224
|
</div>
|
|
220
225
|
<div className="grid grid-cols-3 gap-4">
|
|
221
226
|
<div className="grid gap-2">
|
|
222
|
-
<Label>{
|
|
227
|
+
<Label>{m.common_legalForm_category()}</Label>
|
|
223
228
|
<Input
|
|
224
229
|
value={form.category}
|
|
225
|
-
onChange={(e) => setForm((f) => ({ ...f, category: e.target.value }))}
|
|
226
|
-
placeholder={
|
|
230
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, category: e.target.value }))}
|
|
231
|
+
placeholder={m.common_legalForm_categoryPlaceholder()}
|
|
227
232
|
/>
|
|
228
233
|
</div>
|
|
229
234
|
<div className="grid gap-2">
|
|
230
|
-
<Label>{
|
|
235
|
+
<Label>{m.common_legalForm_region()}</Label>
|
|
231
236
|
<Input
|
|
232
237
|
value={form.region}
|
|
233
|
-
onChange={(e) => setForm((f) => ({ ...f, region: e.target.value }))}
|
|
234
|
-
placeholder={
|
|
238
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, region: e.target.value }))}
|
|
239
|
+
placeholder={m.common_legalForm_regionPlaceholder()}
|
|
235
240
|
/>
|
|
236
241
|
</div>
|
|
237
242
|
<div className="grid gap-2">
|
|
238
|
-
<Label>{
|
|
243
|
+
<Label>{m.common_legalForm_country()}</Label>
|
|
239
244
|
<Input
|
|
240
245
|
value={form.country}
|
|
241
|
-
onChange={(e) => setForm((f) => ({ ...f, country: e.target.value }))}
|
|
242
|
-
placeholder={
|
|
246
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, country: e.target.value }))}
|
|
247
|
+
placeholder={m.common_legalForm_countryPlaceholder()}
|
|
243
248
|
/>
|
|
244
249
|
</div>
|
|
245
250
|
</div>
|
|
246
251
|
<div className="grid grid-cols-2 gap-4">
|
|
247
252
|
<div className="grid gap-2">
|
|
248
|
-
<Label>{
|
|
253
|
+
<Label>{m.common_legalForm_issuingAuthority()}</Label>
|
|
249
254
|
<Input
|
|
250
255
|
value={form.issuingAuthority}
|
|
251
|
-
onChange={(e) => setForm((f) => ({ ...f, issuingAuthority: e.target.value }))}
|
|
252
|
-
placeholder={
|
|
256
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, issuingAuthority: e.target.value }))}
|
|
257
|
+
placeholder={m.common_legalForm_issuingAuthorityPlaceholder()}
|
|
253
258
|
/>
|
|
254
259
|
</div>
|
|
255
260
|
<div className="grid gap-2">
|
|
256
|
-
<Label>{
|
|
261
|
+
<Label>{m.common_legalForm_effectiveDate()}</Label>
|
|
257
262
|
<Input
|
|
258
263
|
type="date"
|
|
259
264
|
value={form.effectiveDate}
|
|
260
|
-
onChange={(e) => setForm((f) => ({ ...f, effectiveDate: e.target.value }))}
|
|
265
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, effectiveDate: e.target.value }))}
|
|
261
266
|
/>
|
|
262
267
|
</div>
|
|
263
268
|
</div>
|
|
264
269
|
<div className="grid gap-2">
|
|
265
|
-
<Label>{
|
|
270
|
+
<Label>{m.common_legalForm_summary()}</Label>
|
|
266
271
|
<Textarea
|
|
267
272
|
value={form.summary}
|
|
268
|
-
onChange={(e) => setForm((f) => ({ ...f, summary: e.target.value }))}
|
|
273
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setForm((f) => ({ ...f, summary: e.target.value }))}
|
|
269
274
|
rows={2}
|
|
270
|
-
placeholder={
|
|
275
|
+
placeholder={m.common_legalForm_summaryPlaceholder()}
|
|
271
276
|
/>
|
|
272
277
|
</div>
|
|
273
278
|
<div className="grid gap-2">
|
|
274
|
-
<Label>{
|
|
279
|
+
<Label>{m.common_legalForm_content()}</Label>
|
|
275
280
|
<Textarea
|
|
276
281
|
value={form.content}
|
|
277
|
-
onChange={(e) => setForm((f) => ({ ...f, content: e.target.value }))}
|
|
282
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setForm((f) => ({ ...f, content: e.target.value }))}
|
|
278
283
|
rows={8}
|
|
279
|
-
placeholder={
|
|
284
|
+
placeholder={m.common_legalForm_contentPlaceholder()}
|
|
280
285
|
className="font-mono text-sm"
|
|
281
286
|
/>
|
|
282
287
|
</div>
|
|
283
288
|
<div className="grid grid-cols-2 gap-4">
|
|
284
289
|
<div className="grid gap-2">
|
|
285
|
-
<Label>{
|
|
290
|
+
<Label>{m.common_legalForm_sourceUrl()}</Label>
|
|
286
291
|
<Input
|
|
287
292
|
value={form.sourceUrl}
|
|
288
|
-
onChange={(e) => setForm((f) => ({ ...f, sourceUrl: e.target.value }))}
|
|
293
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, sourceUrl: e.target.value }))}
|
|
289
294
|
placeholder="https://"
|
|
290
295
|
/>
|
|
291
296
|
</div>
|
|
292
297
|
<div className="grid gap-2">
|
|
293
|
-
<Label>{
|
|
298
|
+
<Label>{m.common_legalForm_tags()}</Label>
|
|
294
299
|
<Input
|
|
295
300
|
value={form.tags}
|
|
296
|
-
onChange={(e) => setForm((f) => ({ ...f, tags: e.target.value }))}
|
|
297
|
-
placeholder={
|
|
301
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, tags: e.target.value }))}
|
|
302
|
+
placeholder={m.common_legalForm_tagsPlaceholder()}
|
|
298
303
|
/>
|
|
299
304
|
</div>
|
|
300
305
|
</div>
|
|
301
306
|
</div>
|
|
302
307
|
<DialogFooter>
|
|
303
308
|
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
|
304
|
-
{
|
|
309
|
+
{m.common_common_cancel()}
|
|
305
310
|
</Button>
|
|
306
311
|
<Button type="submit" disabled={saving}>
|
|
307
|
-
{saving ?
|
|
312
|
+
{saving ? m.common_legalForm_saving() : m.common_button_save()}
|
|
308
313
|
</Button>
|
|
309
314
|
</DialogFooter>
|
|
310
315
|
</form>
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
// You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
|
-
import { useTranslation } from 'react-i18next'
|
|
19
18
|
import {
|
|
20
19
|
Sidebar,
|
|
21
20
|
SidebarContent,
|
|
@@ -25,6 +24,7 @@ import {
|
|
|
25
24
|
useSidebar,
|
|
26
25
|
} from "@/components/ui/sidebar";
|
|
27
26
|
import { cn } from "@/lib/utils";
|
|
27
|
+
import { m } from '@/paraglide/messages.js'
|
|
28
28
|
import { motion } from "motion/react";
|
|
29
29
|
import {
|
|
30
30
|
Home,
|
|
@@ -52,54 +52,54 @@ import DashboardNavigation from "@/components/layout/sidebar-02/nav-main";
|
|
|
52
52
|
import { NotificationsPopover } from "@/components/layout/sidebar-02/nav-notifications";
|
|
53
53
|
import { AdminUserMenu } from "@/components/admin/admin-user-menu";
|
|
54
54
|
|
|
55
|
-
function buildDashboardRoutes(
|
|
55
|
+
function buildDashboardRoutes(): Route[] {
|
|
56
56
|
return [
|
|
57
|
-
{ id: "home", title:
|
|
58
|
-
{ id: "users", title:
|
|
59
|
-
{ id: "docs", title:
|
|
60
|
-
{ id: "apps", title:
|
|
57
|
+
{ id: "home", title: m.common_nav_dashboard(), icon: <Home className="size-4" />, link: "/dashboard" },
|
|
58
|
+
{ id: "users", title: m.common_nav_users(), icon: <Users className="size-4" />, link: "/users" },
|
|
59
|
+
{ id: "docs", title: m.common_nav_docs(), icon: <FileText className="size-4" />, link: "/docs" },
|
|
60
|
+
{ id: "apps", title: m.common_nav_apps(), icon: <Package2 className="size-4" />, link: "/apps" },
|
|
61
61
|
{
|
|
62
|
-
id: "knowledge-public", title:
|
|
62
|
+
id: "knowledge-public", title: m.common_nav_knowledgePublic_title(), icon: <Library className="size-4" />, link: "#",
|
|
63
63
|
subs: [
|
|
64
|
-
{ title:
|
|
65
|
-
{ title:
|
|
66
|
-
{ title:
|
|
64
|
+
{ title: m.common_nav_knowledgePublic_laws(), link: "/knowledge-public/legal", icon: <Scale className="size-4" /> },
|
|
65
|
+
{ title: m.common_nav_knowledgePublic_compliance(), link: "/knowledge-public/compliance", icon: <ShieldCheck className="size-4" /> },
|
|
66
|
+
{ title: m.common_nav_knowledgePublic_bookmarks(), link: "/knowledge-public/bookmarks", icon: <Bookmark className="size-4" /> },
|
|
67
67
|
],
|
|
68
68
|
},
|
|
69
|
-
{ id: "ai-providers", title:
|
|
70
|
-
{ id: "components", title:
|
|
71
|
-
{ id: "dependencies", title:
|
|
72
|
-
{ id: "developer-logs", title:
|
|
73
|
-
{ id: "market", title:
|
|
69
|
+
{ id: "ai-providers", title: m.common_nav_aiProviders(), icon: <Bot className="size-4" />, link: "/ai-providers" },
|
|
70
|
+
{ id: "components", title: m.common_nav_components(), icon: <Layers className="size-4" />, link: "/components" },
|
|
71
|
+
{ id: "dependencies", title: m.common_nav_dependencies(), icon: <Boxes className="size-4" />, link: "/dependencies" },
|
|
72
|
+
{ id: "developer-logs", title: m.common_nav_developerLogs(), icon: <ScrollText className="size-4" />, link: "/developer-logs" },
|
|
73
|
+
{ id: "market", title: m.common_nav_market(), icon: <ShoppingBag className="size-4" />, link: "/market" },
|
|
74
74
|
{
|
|
75
|
-
id: "billing", title:
|
|
75
|
+
id: "billing", title: m.common_nav_billing_title(), icon: <CreditCard className="size-4" />, link: "#",
|
|
76
76
|
subs: [
|
|
77
|
-
{ title:
|
|
78
|
-
{ title:
|
|
77
|
+
{ title: m.common_nav_billing_subscriptions(), link: "/billing/subscriptions" },
|
|
78
|
+
{ title: m.common_nav_billing_invoices(), link: "/billing/invoices" },
|
|
79
79
|
],
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
|
-
id: "enterprise", title:
|
|
82
|
+
id: "enterprise", title: m.common_nav_enterprise_title(), icon: <Shield className="size-4" />, link: "#",
|
|
83
83
|
subs: [
|
|
84
|
-
{ title:
|
|
85
|
-
{ title:
|
|
86
|
-
{ title:
|
|
87
|
-
{ title:
|
|
88
|
-
{ title:
|
|
84
|
+
{ title: m.common_nav_enterprise_licenses(), link: "/licenses" },
|
|
85
|
+
{ title: m.common_nav_enterprise_audit(), link: "/audit" },
|
|
86
|
+
{ title: m.common_nav_enterprise_sso(), link: "/sso" },
|
|
87
|
+
{ title: m.common_nav_enterprise_permissions(), link: "/permissions" },
|
|
88
|
+
{ title: m.common_nav_enterprise_compliance(), link: "/compliance" },
|
|
89
89
|
],
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
|
-
id: "monitoring", title:
|
|
92
|
+
id: "monitoring", title: m.common_nav_monitoring_title(), icon: <Activity className="size-4" />, link: "#",
|
|
93
93
|
subs: [
|
|
94
|
-
{ title:
|
|
95
|
-
{ title:
|
|
96
|
-
{ title:
|
|
97
|
-
{ title:
|
|
98
|
-
{ title:
|
|
99
|
-
{ title:
|
|
94
|
+
{ title: m.common_nav_monitoring_overview(), link: "/monitoring/overview" },
|
|
95
|
+
{ title: m.common_nav_monitoring_logs(), link: "/monitoring/logs" },
|
|
96
|
+
{ title: m.common_nav_monitoring_errors(), link: "/monitoring/errors" },
|
|
97
|
+
{ title: m.common_nav_monitoring_metrics(), link: "/monitoring/metrics" },
|
|
98
|
+
{ title: m.common_nav_monitoring_health(), link: "/monitoring/health" },
|
|
99
|
+
{ title: m.common_nav_monitoring_sentry(), link: "/monitoring/sentry" },
|
|
100
100
|
],
|
|
101
101
|
},
|
|
102
|
-
{ id: "settings", title:
|
|
102
|
+
{ id: "settings", title: m.common_nav_settings(), icon: <Settings className="size-4" />, link: "/settings" },
|
|
103
103
|
]
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -110,10 +110,9 @@ const teams = [
|
|
|
110
110
|
];
|
|
111
111
|
|
|
112
112
|
export function DashboardSidebar({ routes: propRoutes }: { routes?: Route[] }) {
|
|
113
|
-
const { t } = useTranslation('common')
|
|
114
113
|
const { state } = useSidebar();
|
|
115
114
|
const isCollapsed = state === "collapsed";
|
|
116
|
-
const routes = propRoutes || buildDashboardRoutes(
|
|
115
|
+
const routes = propRoutes || buildDashboardRoutes();
|
|
117
116
|
|
|
118
117
|
return (
|
|
119
118
|
<Sidebar variant="inset" collapsible="icon">
|
|
@@ -73,7 +73,7 @@ export default function DashboardNavigation({ routes }: { routes: Route[] }) {
|
|
|
73
73
|
{hasSubRoutes ? (
|
|
74
74
|
<Collapsible
|
|
75
75
|
open={isOpen}
|
|
76
|
-
onOpenChange={(open) =>
|
|
76
|
+
onOpenChange={(open: boolean) =>
|
|
77
77
|
setOpenCollapsible(open ? route.id : null)
|
|
78
78
|
}
|
|
79
79
|
className="w-full"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import type { AdminColumnDef } from "@/components/data-table/data-table";
|
|
19
|
-
import {
|
|
19
|
+
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import { Badge } from "@/components/ui/badge"
|
|
21
21
|
import { Button } from "@/components/ui/button"
|
|
22
22
|
import {
|
|
@@ -50,11 +50,10 @@ export function getMarketColumns(
|
|
|
50
50
|
onToggleStatus: (app: MarketAppRow) => void,
|
|
51
51
|
onDelete: (app: MarketAppRow) => void
|
|
52
52
|
): AdminColumnDef<MarketAppRow>[] {
|
|
53
|
-
const { t } = useTranslation('common')
|
|
54
53
|
const statusLabel: Record<string, string> = {
|
|
55
|
-
PUBLISHED:
|
|
56
|
-
DRAFT:
|
|
57
|
-
UNPUBLISHED:
|
|
54
|
+
PUBLISHED: m.common_market_statusLabel_published(),
|
|
55
|
+
DRAFT: m.common_market_statusLabel_draft(),
|
|
56
|
+
UNPUBLISHED: m.common_market_statusLabel_unpublished(),
|
|
58
57
|
}
|
|
59
58
|
const statusBadgeVariant: Record<string, "default" | "secondary" | "destructive" | "outline"> = {
|
|
60
59
|
PUBLISHED: "default",
|
|
@@ -65,8 +64,8 @@ export function getMarketColumns(
|
|
|
65
64
|
return [
|
|
66
65
|
{
|
|
67
66
|
accessorKey: "displayName",
|
|
68
|
-
header:
|
|
69
|
-
cell: ({ row }) => {
|
|
67
|
+
header: m.common_market_columns_appName(),
|
|
68
|
+
cell: ({ row }: any) => {
|
|
70
69
|
const app = row.original
|
|
71
70
|
return (
|
|
72
71
|
<div>
|
|
@@ -78,13 +77,13 @@ export function getMarketColumns(
|
|
|
78
77
|
},
|
|
79
78
|
{
|
|
80
79
|
accessorKey: "category",
|
|
81
|
-
header:
|
|
82
|
-
cell: ({ row }) => row.original.category || "-",
|
|
80
|
+
header: m.common_market_columns_category(),
|
|
81
|
+
cell: ({ row }: any) => row.original.category || "-",
|
|
83
82
|
},
|
|
84
83
|
{
|
|
85
84
|
accessorKey: "entryUrl",
|
|
86
|
-
header:
|
|
87
|
-
cell: ({ row }) => (
|
|
85
|
+
header: m.common_market_columns_entryUrl(),
|
|
86
|
+
cell: ({ row }: any) => (
|
|
88
87
|
<span className="max-w-48 truncate text-sm text-muted-foreground">
|
|
89
88
|
{row.original.entryUrl}
|
|
90
89
|
</span>
|
|
@@ -92,25 +91,25 @@ export function getMarketColumns(
|
|
|
92
91
|
},
|
|
93
92
|
{
|
|
94
93
|
accessorKey: "downloads",
|
|
95
|
-
header:
|
|
94
|
+
header: m.common_market_columns_downloads(),
|
|
96
95
|
},
|
|
97
96
|
{
|
|
98
97
|
accessorKey: "status",
|
|
99
|
-
header:
|
|
100
|
-
cell: ({ row }) => {
|
|
98
|
+
header: m.common_market_columns_status(),
|
|
99
|
+
cell: ({ row }: any) => {
|
|
101
100
|
const status = row.original.status
|
|
102
101
|
return <Badge variant={statusBadgeVariant[status] || "secondary"}>{statusLabel[status] || status}</Badge>
|
|
103
102
|
},
|
|
104
103
|
},
|
|
105
104
|
{
|
|
106
105
|
accessorKey: "createdAt",
|
|
107
|
-
header:
|
|
108
|
-
cell: ({ row }) => new Date(row.original.createdAt).toLocaleDateString("zh-CN"),
|
|
106
|
+
header: m.common_market_columns_createdAt(),
|
|
107
|
+
cell: ({ row }: any) => new Date(row.original.createdAt).toLocaleDateString("zh-CN"),
|
|
109
108
|
},
|
|
110
109
|
{
|
|
111
110
|
id: "actions",
|
|
112
111
|
header: "",
|
|
113
|
-
cell: ({ row }) => {
|
|
112
|
+
cell: ({ row }: any) => {
|
|
114
113
|
const app = row.original
|
|
115
114
|
return (
|
|
116
115
|
<DropdownMenu>
|
|
@@ -122,25 +121,25 @@ export function getMarketColumns(
|
|
|
122
121
|
<DropdownMenuContent align="end">
|
|
123
122
|
<DropdownMenuItem onClick={() => onEdit(app)}>
|
|
124
123
|
<PencilIcon className="mr-2 size-4" />
|
|
125
|
-
{
|
|
124
|
+
{m.common_common_edit()}
|
|
126
125
|
</DropdownMenuItem>
|
|
127
126
|
<DropdownMenuItem onClick={() => onToggleStatus(app)}>
|
|
128
127
|
{app.status === "PUBLISHED" ? (
|
|
129
128
|
<>
|
|
130
129
|
<EyeOffIcon className="mr-2 size-4" />
|
|
131
|
-
{
|
|
130
|
+
{m.common_market_unpublish()}
|
|
132
131
|
</>
|
|
133
132
|
) : (
|
|
134
133
|
<>
|
|
135
134
|
<EyeIcon className="mr-2 size-4" />
|
|
136
|
-
{
|
|
135
|
+
{m.common_market_publish()}
|
|
137
136
|
</>
|
|
138
137
|
)}
|
|
139
138
|
</DropdownMenuItem>
|
|
140
139
|
<DropdownMenuSeparator />
|
|
141
140
|
<DropdownMenuItem className="text-destructive" onClick={() => onDelete(app)}>
|
|
142
141
|
<TrashIcon className="mr-2 size-4" />
|
|
143
|
-
{
|
|
142
|
+
{m.common_common_delete()}
|
|
144
143
|
</DropdownMenuItem>
|
|
145
144
|
</DropdownMenuContent>
|
|
146
145
|
</DropdownMenu>
|