@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.
Files changed (86) hide show
  1. package/package.json +9 -9
  2. package/src/components/admin/admin-user-menu.tsx +12 -6
  3. package/src/components/admin/developer-log.tsx +42 -44
  4. package/src/components/ai-provider/model-list-editor.tsx +5 -4
  5. package/src/components/ai-provider/provider-form.tsx +63 -64
  6. package/src/components/ai-provider/provider-table.tsx +35 -36
  7. package/src/components/auth/admin-login-form.tsx +23 -24
  8. package/src/components/auth/oauth-section.tsx +3 -5
  9. package/src/components/auth/password-input.tsx +2 -3
  10. package/src/components/common/placeholder-page.tsx +2 -4
  11. package/src/components/component/component-form-dialog.tsx +48 -49
  12. package/src/components/component/component-import-dialog.tsx +5 -5
  13. package/src/components/component/component-table.tsx +13 -14
  14. package/src/components/data-table/data-table-pagination.tsx +4 -6
  15. package/src/components/data-table/table-detail-layout.tsx +2 -3
  16. package/src/components/dependency/changelog-dialog.tsx +3 -4
  17. package/src/components/dependency/columns.tsx +32 -24
  18. package/src/components/dependency/dependency-table.tsx +37 -29
  19. package/src/components/dependency/edit-dialog.tsx +24 -25
  20. package/src/components/knowledge-public/bookmark-form-dialog.tsx +7 -7
  21. package/src/components/knowledge-public/compliance-form-dialog.tsx +13 -13
  22. package/src/components/knowledge-public/legal-form-dialog.tsx +51 -46
  23. package/src/components/layout/sidebar-02/app-sidebar.tsx +33 -34
  24. package/src/components/layout/sidebar-02/nav-main.tsx +1 -1
  25. package/src/components/market/columns.tsx +20 -21
  26. package/src/components/market/market-app-form.tsx +45 -38
  27. package/src/components/market/market-app-list.tsx +16 -17
  28. package/src/components/monitoring/error-list.tsx +18 -19
  29. package/src/components/monitoring/health-cards.tsx +11 -13
  30. package/src/components/monitoring/metrics-stats.tsx +11 -12
  31. package/src/components/monitoring/request-log-table.tsx +8 -9
  32. package/src/components/ui/pagination.tsx +0 -2
  33. package/src/components/ui/sidebar.tsx +1 -1
  34. package/src/components/user/columns.tsx +9 -9
  35. package/src/components/user/settings-activity-log.tsx +39 -29
  36. package/src/components/user/settings-profile.tsx +36 -36
  37. package/src/components/user/user-create-dialog.tsx +4 -4
  38. package/src/components/user/user-filters.tsx +16 -18
  39. package/src/lib/i18n/config.ts +0 -32
  40. package/src/providers/index.tsx +6 -16
  41. package/src/routes/__root.tsx +9 -23
  42. package/src/routes/_nav/apps/route.lazy.tsx +2 -3
  43. package/src/routes/_nav/audit/route.lazy.tsx +10 -11
  44. package/src/routes/_nav/billing/invoices/route.lazy.tsx +6 -7
  45. package/src/routes/_nav/billing/route.lazy.tsx +3 -4
  46. package/src/routes/_nav/billing/subscriptions/$subscriptionId/route.lazy.tsx +16 -17
  47. package/src/routes/_nav/billing/subscriptions/route.lazy.tsx +9 -10
  48. package/src/routes/_nav/compliance/route.lazy.tsx +34 -35
  49. package/src/routes/_nav/components/route.lazy.tsx +6 -6
  50. package/src/routes/_nav/dashboard/route.lazy.tsx +3 -5
  51. package/src/routes/_nav/dependencies/route.lazy.tsx +7 -8
  52. package/src/routes/_nav/docs/route.lazy.tsx +2 -3
  53. package/src/routes/_nav/knowledge-public/bookmarks/route.lazy.tsx +40 -41
  54. package/src/routes/_nav/knowledge-public/compliance/route.lazy.tsx +32 -33
  55. package/src/routes/_nav/knowledge-public/legal/route.lazy.tsx +32 -33
  56. package/src/routes/_nav/licenses/$licenseId/route.lazy.tsx +24 -31
  57. package/src/routes/_nav/licenses/new/route.lazy.tsx +25 -26
  58. package/src/routes/_nav/licenses/route.lazy.tsx +17 -18
  59. package/src/routes/_nav/monitoring/ai-usage/route.lazy.tsx +26 -27
  60. package/src/routes/_nav/monitoring/errors/route.lazy.tsx +11 -12
  61. package/src/routes/_nav/monitoring/health/route.lazy.tsx +18 -20
  62. package/src/routes/_nav/monitoring/logs/route.lazy.tsx +12 -13
  63. package/src/routes/_nav/monitoring/metrics/route.lazy.tsx +13 -15
  64. package/src/routes/_nav/monitoring/overview/route.lazy.tsx +12 -13
  65. package/src/routes/_nav/monitoring/route.lazy.tsx +8 -9
  66. package/src/routes/_nav/monitoring/sentry/route.lazy.tsx +55 -57
  67. package/src/routes/_nav/permissions/route.lazy.tsx +48 -50
  68. package/src/routes/_nav/settings/route.lazy.tsx +2 -3
  69. package/src/routes/_nav/sso/route.lazy.tsx +20 -18
  70. package/src/routes/_nav/users/$userId/route.lazy.tsx +86 -82
  71. package/src/routes/_nav/users/route.lazy.tsx +4 -5
  72. package/src/routes/setup/route.lazy.tsx +15 -16
  73. package/src/start.ts +2 -2
  74. package/src/lib/i18n/client.ts +0 -32
  75. package/src/lib/i18n/locales/en/admin-ai.json +0 -128
  76. package/src/lib/i18n/locales/en/admin-billing.json +0 -56
  77. package/src/lib/i18n/locales/en/admin-users.json +0 -88
  78. package/src/lib/i18n/locales/en/common.json +0 -877
  79. package/src/lib/i18n/locales/en/enterprise.json +0 -208
  80. package/src/lib/i18n/locales/zh/admin-ai.json +0 -128
  81. package/src/lib/i18n/locales/zh/admin-billing.json +0 -56
  82. package/src/lib/i18n/locales/zh/admin-users.json +0 -88
  83. package/src/lib/i18n/locales/zh/common.json +0 -877
  84. package/src/lib/i18n/locales/zh/enterprise.json +0 -208
  85. package/src/lib/i18n/server.ts +0 -18
  86. 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 { useTranslation } from 'react-i18next'
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 : t('legalForm.loadFailed')))
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(t('legalForm.titleRequired'))
137
+ toast.error(m.common_legalForm_titleRequired())
133
138
  return
134
139
  }
135
140
  if (!form.category.trim()) {
136
- toast.error(t('legalForm.categoryRequired'))
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(t('legalForm.updated'))
162
+ toast.success(m.common_legalForm_updated())
158
163
  } else {
159
164
  await adminCreateLegalFn({ data: payload })
160
- toast.success(t('legalForm.created'))
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 : t('legalForm.saveFailed'))
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 ? t('legalForm.edit') : t('legalForm.create')}</DialogTitle>
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>{t('legalForm.title')}</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={t('legalForm.titlePlaceholder')}
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>{t('legalForm.code')}</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={t('legalForm.codePlaceholder')}
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>{t('legalForm.jurisdiction')}</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
- {t(`legalForm.jurisdictions.${j.value}`)}
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>{t('legalForm.category')}</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={t('legalForm.categoryPlaceholder')}
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>{t('legalForm.region')}</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={t('legalForm.regionPlaceholder')}
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>{t('legalForm.country')}</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={t('legalForm.countryPlaceholder')}
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>{t('legalForm.issuingAuthority')}</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={t('legalForm.issuingAuthorityPlaceholder')}
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>{t('legalForm.effectiveDate')}</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>{t('legalForm.summary')}</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={t('legalForm.summaryPlaceholder')}
275
+ placeholder={m.common_legalForm_summaryPlaceholder()}
271
276
  />
272
277
  </div>
273
278
  <div className="grid gap-2">
274
- <Label>{t('legalForm.content')}</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={t('legalForm.contentPlaceholder')}
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>{t('legalForm.sourceUrl')}</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>{t('legalForm.tags')}</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={t('legalForm.tagsPlaceholder')}
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
- {t('common.cancel')}
309
+ {m.common_common_cancel()}
305
310
  </Button>
306
311
  <Button type="submit" disabled={saving}>
307
- {saving ? t('legalForm.saving') : t('common.save')}
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(t: (key: string) => string): Route[] {
55
+ function buildDashboardRoutes(): Route[] {
56
56
  return [
57
- { id: "home", title: t('nav.dashboard'), icon: <Home className="size-4" />, link: "/dashboard" },
58
- { id: "users", title: t('nav.users'), icon: <Users className="size-4" />, link: "/users" },
59
- { id: "docs", title: t('nav.docs'), icon: <FileText className="size-4" />, link: "/docs" },
60
- { id: "apps", title: t('nav.apps'), icon: <Package2 className="size-4" />, link: "/apps" },
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: t('nav.knowledgePublic.title'), icon: <Library className="size-4" />, link: "#",
62
+ id: "knowledge-public", title: m.common_nav_knowledgePublic_title(), icon: <Library className="size-4" />, link: "#",
63
63
  subs: [
64
- { title: t('nav.knowledgePublic.laws'), link: "/knowledge-public/legal", icon: <Scale className="size-4" /> },
65
- { title: t('nav.knowledgePublic.compliance'), link: "/knowledge-public/compliance", icon: <ShieldCheck className="size-4" /> },
66
- { title: t('nav.knowledgePublic.bookmarks'), link: "/knowledge-public/bookmarks", icon: <Bookmark className="size-4" /> },
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: t('nav.aiProviders'), icon: <Bot className="size-4" />, link: "/ai-providers" },
70
- { id: "components", title: t('nav.components'), icon: <Layers className="size-4" />, link: "/components" },
71
- { id: "dependencies", title: t('nav.dependencies'), icon: <Boxes className="size-4" />, link: "/dependencies" },
72
- { id: "developer-logs", title: t('nav.developerLogs'), icon: <ScrollText className="size-4" />, link: "/developer-logs" },
73
- { id: "market", title: t('nav.market'), icon: <ShoppingBag className="size-4" />, link: "/market" },
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: t('nav.billing.title'), icon: <CreditCard className="size-4" />, link: "#",
75
+ id: "billing", title: m.common_nav_billing_title(), icon: <CreditCard className="size-4" />, link: "#",
76
76
  subs: [
77
- { title: t('nav.billing.subscriptions'), link: "/billing/subscriptions" },
78
- { title: t('nav.billing.invoices'), link: "/billing/invoices" },
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: t('nav.enterprise.title'), icon: <Shield className="size-4" />, link: "#",
82
+ id: "enterprise", title: m.common_nav_enterprise_title(), icon: <Shield className="size-4" />, link: "#",
83
83
  subs: [
84
- { title: t('nav.enterprise.licenses'), link: "/licenses" },
85
- { title: t('nav.enterprise.audit'), link: "/audit" },
86
- { title: t('nav.enterprise.sso'), link: "/sso" },
87
- { title: t('nav.enterprise.permissions'), link: "/permissions" },
88
- { title: t('nav.enterprise.compliance'), link: "/compliance" },
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: t('nav.monitoring.title'), icon: <Activity className="size-4" />, link: "#",
92
+ id: "monitoring", title: m.common_nav_monitoring_title(), icon: <Activity className="size-4" />, link: "#",
93
93
  subs: [
94
- { title: t('nav.monitoring.overview'), link: "/monitoring/overview" },
95
- { title: t('nav.monitoring.logs'), link: "/monitoring/logs" },
96
- { title: t('nav.monitoring.errors'), link: "/monitoring/errors" },
97
- { title: t('nav.monitoring.metrics'), link: "/monitoring/metrics" },
98
- { title: t('nav.monitoring.health'), link: "/monitoring/health" },
99
- { title: t('nav.monitoring.sentry'), link: "/monitoring/sentry" },
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: t('nav.settings'), icon: <Settings className="size-4" />, link: "/settings" },
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(t);
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 { useTranslation } from 'react-i18next'
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: t('market.statusLabel.published'),
56
- DRAFT: t('market.statusLabel.draft'),
57
- UNPUBLISHED: t('market.statusLabel.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: t('market.columns.appName'),
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: t('market.columns.category'),
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: t('market.columns.entryUrl'),
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: t('market.columns.downloads'),
94
+ header: m.common_market_columns_downloads(),
96
95
  },
97
96
  {
98
97
  accessorKey: "status",
99
- header: t('market.columns.status'),
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: t('market.columns.createdAt'),
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
- {t('common.edit')}
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
- {t('market.unpublish')}
130
+ {m.common_market_unpublish()}
132
131
  </>
133
132
  ) : (
134
133
  <>
135
134
  <EyeIcon className="mr-2 size-4" />
136
- {t('market.publish')}
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
- {t('common.delete')}
142
+ {m.common_common_delete()}
144
143
  </DropdownMenuItem>
145
144
  </DropdownMenuContent>
146
145
  </DropdownMenu>