@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
|
@@ -24,8 +24,7 @@ import {
|
|
|
24
24
|
PaginationNext,
|
|
25
25
|
PaginationPrevious,
|
|
26
26
|
} from "@/components/ui/pagination";
|
|
27
|
-
import {
|
|
28
|
-
|
|
27
|
+
import { m } from '@/paraglide/messages.js'
|
|
29
28
|
interface DataTablePaginationProps {
|
|
30
29
|
page: number;
|
|
31
30
|
totalPages: number;
|
|
@@ -66,19 +65,18 @@ export function DataTablePagination({
|
|
|
66
65
|
total,
|
|
67
66
|
onPageChange,
|
|
68
67
|
}: DataTablePaginationProps) {
|
|
69
|
-
const { t } = useTranslation('common')
|
|
70
68
|
const pages = getPageNumbers(page, totalPages);
|
|
71
69
|
|
|
72
70
|
return (
|
|
73
71
|
<div className="flex items-center justify-between gap-4 px-2 py-4">
|
|
74
72
|
<div className="text-sm text-muted-foreground whitespace-nowrap shrink-0">
|
|
75
|
-
{
|
|
73
|
+
{m.common_pagination_total({ total, page, totalPages })}
|
|
76
74
|
</div>
|
|
77
75
|
<Pagination className="justify-end">
|
|
78
76
|
<PaginationContent>
|
|
79
77
|
<PaginationItem>
|
|
80
78
|
<PaginationPrevious
|
|
81
|
-
text={
|
|
79
|
+
text={m.common_pagination_prev()}
|
|
82
80
|
onClick={() => page > 1 && onPageChange(page - 1)}
|
|
83
81
|
className={page <= 1 ? "pointer-events-none opacity-50" : "cursor-pointer"}
|
|
84
82
|
/>
|
|
@@ -102,7 +100,7 @@ export function DataTablePagination({
|
|
|
102
100
|
)}
|
|
103
101
|
<PaginationItem>
|
|
104
102
|
<PaginationNext
|
|
105
|
-
text={
|
|
103
|
+
text={m.common_pagination_next()}
|
|
106
104
|
onClick={() => page < totalPages && onPageChange(page + 1)}
|
|
107
105
|
className={page >= totalPages ? "pointer-events-none opacity-50" : "cursor-pointer"}
|
|
108
106
|
/>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { useNavigate } from "@tanstack/react-router";
|
|
19
|
-
import {
|
|
19
|
+
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import { Button } from "@/components/ui/button";
|
|
21
21
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
22
22
|
import { ArrowLeftIcon } from "lucide-react";
|
|
@@ -44,9 +44,8 @@ export function TableDetailLayout({
|
|
|
44
44
|
loading,
|
|
45
45
|
children,
|
|
46
46
|
}: TableDetailLayoutProps) {
|
|
47
|
-
const { t } = useTranslation('common')
|
|
48
47
|
const navigate = useNavigate();
|
|
49
|
-
const resolvedBackLabel = backLabel ??
|
|
48
|
+
const resolvedBackLabel = backLabel ?? m.common_common_backToList()
|
|
50
49
|
|
|
51
50
|
if (loading) {
|
|
52
51
|
return (
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { useState } from "react"
|
|
19
|
-
import {
|
|
19
|
+
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import ReactMarkdown from "react-markdown"
|
|
21
21
|
import remarkGfm from "remark-gfm"
|
|
22
22
|
import { ExternalLink, FileText } from "lucide-react"
|
|
@@ -32,7 +32,6 @@ import {
|
|
|
32
32
|
|
|
33
33
|
export function ChangelogDialog({ row }: { row: ThirdPartyDependencyRow }) {
|
|
34
34
|
const [open, setOpen] = useState(false)
|
|
35
|
-
const { t } = useTranslation('common')
|
|
36
35
|
const hasChangelog = !!row.latestChangelog
|
|
37
36
|
|
|
38
37
|
if (!hasChangelog) {
|
|
@@ -42,9 +41,9 @@ export function ChangelogDialog({ row }: { row: ThirdPartyDependencyRow }) {
|
|
|
42
41
|
return (
|
|
43
42
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
44
43
|
<DialogTrigger asChild>
|
|
45
|
-
<Button size="sm" variant="ghost" className="h-7 gap-1 px-2" title={
|
|
44
|
+
<Button size="sm" variant="ghost" className="h-7 gap-1 px-2" title={m.common_dependency_table_cell_viewChangelog()}>
|
|
46
45
|
<FileText className="size-3.5" />
|
|
47
|
-
<span className="text-xs">{
|
|
46
|
+
<span className="text-xs">{m.common_dependency_table_cell_changelogBtn()}</span>
|
|
48
47
|
</Button>
|
|
49
48
|
</DialogTrigger>
|
|
50
49
|
<DialogContent className="max-h-[85vh] w-200 max-w-4xl overflow-hidden p-0">
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
type ColumnDef,
|
|
24
24
|
} from "@tanstack/react-table"
|
|
25
25
|
import { CheckCircle2, ExternalLink, Loader2, RefreshCw } from "lucide-react"
|
|
26
|
+
import { m } from '@/paraglide/messages.js'
|
|
26
27
|
import {
|
|
27
28
|
DEPENDENCY_CATEGORIES,
|
|
28
29
|
type ThirdPartyDependencyRow,
|
|
@@ -49,37 +50,44 @@ export const STATUS_META: Record<VersionStatus, { className: string }> = {
|
|
|
49
50
|
unknown: { className: "bg-muted text-muted-foreground border-border" },
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
const STATUS_LABELS: Record<string, () => string> = {
|
|
54
|
+
latest: m.common_dependency_statusLabel_latest,
|
|
55
|
+
patch: m.common_dependency_statusLabel_patch,
|
|
56
|
+
minor: m.common_dependency_statusLabel_minor,
|
|
57
|
+
major: m.common_dependency_statusLabel_major,
|
|
58
|
+
unknown: m.common_dependency_statusLabel_unknown,
|
|
54
59
|
}
|
|
55
60
|
|
|
56
|
-
export function
|
|
61
|
+
export function getDependencyStatusLabel(status: VersionStatus) {
|
|
62
|
+
return (STATUS_LABELS[status] ?? (() => status))()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getDependencyTimeAgo(iso: string | null): string {
|
|
57
66
|
if (!iso) return "—"
|
|
58
67
|
const d = new Date(iso)
|
|
59
68
|
if (Number.isNaN(d.getTime())) return "—"
|
|
60
69
|
const diff = Date.now() - d.getTime()
|
|
61
70
|
const min = Math.floor(diff / 60000)
|
|
62
|
-
if (min < 1) return
|
|
63
|
-
if (min < 60) return
|
|
71
|
+
if (min < 1) return m.common_dependency_timeAgo_justNow()
|
|
72
|
+
if (min < 60) return m.common_dependency_timeAgo_minutes({ count: min })
|
|
64
73
|
const hr = Math.floor(min / 60)
|
|
65
|
-
if (hr < 24) return
|
|
74
|
+
if (hr < 24) return m.common_dependency_timeAgo_hours({ count: hr })
|
|
66
75
|
const day = Math.floor(hr / 24)
|
|
67
|
-
return
|
|
76
|
+
return m.common_dependency_timeAgo_days({ count: day })
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
interface ColumnOpts {
|
|
71
80
|
syncing: Set<string>
|
|
72
81
|
onSync: (name: string) => void
|
|
73
82
|
onSaved: () => void
|
|
74
|
-
t: (k: string, params?: Record<string, unknown>) => string
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
export function getDependencyColumns(opts: ColumnOpts): ColumnDef<typeof dependencyTableFeatures, ThirdPartyDependencyRow>[] {
|
|
78
|
-
const { syncing, onSync, onSaved
|
|
86
|
+
const { syncing, onSync, onSaved } = opts
|
|
79
87
|
return [
|
|
80
88
|
{
|
|
81
89
|
accessorKey: "displayName",
|
|
82
|
-
header:
|
|
90
|
+
header: m.common_dependency_table_columns_dependency(),
|
|
83
91
|
cell: ({ row }) => {
|
|
84
92
|
const r = row.original
|
|
85
93
|
return (
|
|
@@ -88,12 +96,12 @@ export function getDependencyColumns(opts: ColumnOpts): ColumnDef<typeof depende
|
|
|
88
96
|
<span className="font-medium">{r.displayName}</span>
|
|
89
97
|
{!r.isWatched && (
|
|
90
98
|
<Badge variant="outline" className="px-1 py-0 text-[10px] text-muted-foreground">
|
|
91
|
-
{
|
|
99
|
+
{m.common_dependency_table_cell_untracked()}
|
|
92
100
|
</Badge>
|
|
93
101
|
)}
|
|
94
102
|
{r.priority === "high" && r.isWatched && (
|
|
95
103
|
<Badge variant="outline" className={cn("px-1 py-0 text-[10px]", PRIORITY_BADGE.high)}>
|
|
96
|
-
{
|
|
104
|
+
{m.common_dependency_table_cell_highPriority()}
|
|
97
105
|
</Badge>
|
|
98
106
|
)}
|
|
99
107
|
</div>
|
|
@@ -114,7 +122,7 @@ export function getDependencyColumns(opts: ColumnOpts): ColumnDef<typeof depende
|
|
|
114
122
|
},
|
|
115
123
|
{
|
|
116
124
|
accessorKey: "description",
|
|
117
|
-
header:
|
|
125
|
+
header: m.common_dependency_table_columns_description(),
|
|
118
126
|
cell: ({ row }) => {
|
|
119
127
|
const desc = row.original.description
|
|
120
128
|
if (!desc) return <span className="text-xs text-muted-foreground">—</span>
|
|
@@ -132,7 +140,7 @@ export function getDependencyColumns(opts: ColumnOpts): ColumnDef<typeof depende
|
|
|
132
140
|
},
|
|
133
141
|
{
|
|
134
142
|
accessorKey: "category",
|
|
135
|
-
header:
|
|
143
|
+
header: m.common_dependency_table_columns_category(),
|
|
136
144
|
cell: ({ row }) => (
|
|
137
145
|
<Badge variant="outline" className="font-normal">
|
|
138
146
|
{DEPENDENCY_CATEGORIES.find((c) => c.value === row.original.category)?.label ?? row.original.category}
|
|
@@ -141,10 +149,10 @@ export function getDependencyColumns(opts: ColumnOpts): ColumnDef<typeof depende
|
|
|
141
149
|
},
|
|
142
150
|
{
|
|
143
151
|
accessorKey: "currentVersion",
|
|
144
|
-
header:
|
|
152
|
+
header: m.common_dependency_table_columns_currentVersion(),
|
|
145
153
|
cell: ({ row }) => {
|
|
146
154
|
const r = row.original
|
|
147
|
-
if (!r.currentVersion) return <span className="text-xs text-muted-foreground">{
|
|
155
|
+
if (!r.currentVersion) return <span className="text-xs text-muted-foreground">{m.common_dependency_table_cell_notInstalled()}</span>
|
|
148
156
|
if (r.usages.length > 1) {
|
|
149
157
|
return (
|
|
150
158
|
<Tooltip>
|
|
@@ -170,38 +178,38 @@ export function getDependencyColumns(opts: ColumnOpts): ColumnDef<typeof depende
|
|
|
170
178
|
},
|
|
171
179
|
{
|
|
172
180
|
accessorKey: "latestVersion",
|
|
173
|
-
header:
|
|
181
|
+
header: m.common_dependency_table_columns_latestVersion(),
|
|
174
182
|
cell: ({ row }) =>
|
|
175
183
|
row.original.latestVersion ?? <span className="text-muted-foreground">—</span>,
|
|
176
184
|
},
|
|
177
185
|
{
|
|
178
186
|
id: "changelog",
|
|
179
|
-
header:
|
|
187
|
+
header: m.common_dependency_table_columns_changelog(),
|
|
180
188
|
cell: ({ row }) => <ChangelogDialog row={row.original} />,
|
|
181
189
|
},
|
|
182
190
|
{
|
|
183
191
|
accessorKey: "versionStatus",
|
|
184
|
-
header:
|
|
192
|
+
header: m.common_dependency_table_columns_status(),
|
|
185
193
|
cell: ({ row }) => {
|
|
186
194
|
const status = row.original.versionStatus
|
|
187
195
|
return (
|
|
188
196
|
<Badge variant="outline" className={cn("font-normal", STATUS_META[status].className)}>
|
|
189
197
|
{status === "latest" && <CheckCircle2 className="mr-1 size-3" />}
|
|
190
|
-
{getDependencyStatusLabel(
|
|
198
|
+
{getDependencyStatusLabel(status)}
|
|
191
199
|
</Badge>
|
|
192
200
|
)
|
|
193
201
|
},
|
|
194
202
|
},
|
|
195
203
|
{
|
|
196
204
|
accessorKey: "lastSyncAt",
|
|
197
|
-
header:
|
|
205
|
+
header: m.common_dependency_table_columns_lastSync(),
|
|
198
206
|
cell: ({ row }) => (
|
|
199
|
-
<span className="text-xs text-muted-foreground">{getDependencyTimeAgo(
|
|
207
|
+
<span className="text-xs text-muted-foreground">{getDependencyTimeAgo(row.original.lastSyncAt)}</span>
|
|
200
208
|
),
|
|
201
209
|
},
|
|
202
210
|
{
|
|
203
211
|
id: "actions",
|
|
204
|
-
header: () => <div className="text-right">{
|
|
212
|
+
header: () => <div className="text-right">{m.common_dependency_table_columns_actions()}</div>,
|
|
205
213
|
cell: ({ row }) => {
|
|
206
214
|
const r = row.original
|
|
207
215
|
const isSyncing = syncing.has(r.name)
|
|
@@ -213,7 +221,7 @@ export function getDependencyColumns(opts: ColumnOpts): ColumnDef<typeof depende
|
|
|
213
221
|
className="size-7 p-0"
|
|
214
222
|
onClick={() => onSync(r.name)}
|
|
215
223
|
disabled={isSyncing}
|
|
216
|
-
title={
|
|
224
|
+
title={m.common_dependency_table_cell_syncTooltip()}
|
|
217
225
|
>
|
|
218
226
|
{isSyncing ? <Loader2 className="size-3.5 animate-spin" /> : <RefreshCw className="size-3.5" />}
|
|
219
227
|
</Button>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { useMemo, useState } from "react"
|
|
19
|
-
import {
|
|
19
|
+
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import {
|
|
21
21
|
flexRender,
|
|
22
22
|
useTable,
|
|
@@ -66,6 +66,15 @@ const STATUS_FILTERS: Array<{ value: "all" | VersionStatus }> = [
|
|
|
66
66
|
{ value: "unknown" },
|
|
67
67
|
]
|
|
68
68
|
|
|
69
|
+
const STATUS_FILTER_LABELS: Record<typeof STATUS_FILTERS[number]['value'], () => string> = {
|
|
70
|
+
all: m.common_dependency_table_statusFilter_all,
|
|
71
|
+
latest: m.common_dependency_table_statusFilter_latest,
|
|
72
|
+
patch: m.common_dependency_table_statusFilter_patch,
|
|
73
|
+
minor: m.common_dependency_table_statusFilter_minor,
|
|
74
|
+
major: m.common_dependency_table_statusFilter_major,
|
|
75
|
+
unknown: m.common_dependency_table_statusFilter_unknown,
|
|
76
|
+
}
|
|
77
|
+
|
|
69
78
|
export function DependencyTable({
|
|
70
79
|
data,
|
|
71
80
|
onMutated,
|
|
@@ -73,7 +82,6 @@ export function DependencyTable({
|
|
|
73
82
|
data: ThirdPartyDependencyRow[]
|
|
74
83
|
onMutated?: () => void
|
|
75
84
|
}) {
|
|
76
|
-
const { t } = useTranslation('common')
|
|
77
85
|
const [search, setSearch] = useState("")
|
|
78
86
|
const [categoryFilter, setCategoryFilter] = useState<"all" | DependencyCategory>("all")
|
|
79
87
|
const [statusFilter, setStatusFilter] = useState<"all" | VersionStatus>("all")
|
|
@@ -100,8 +108,8 @@ export function DependencyTable({
|
|
|
100
108
|
}, [data, search, categoryFilter, statusFilter, viewFilter])
|
|
101
109
|
|
|
102
110
|
const columns = useMemo(
|
|
103
|
-
() => getDependencyColumns({ syncing, onSync: handleSync, onSaved: () => onMutated?.()
|
|
104
|
-
[syncing, onMutated
|
|
111
|
+
() => getDependencyColumns({ syncing, onSync: handleSync, onSaved: () => onMutated?.() }),
|
|
112
|
+
[syncing, onMutated],
|
|
105
113
|
)
|
|
106
114
|
|
|
107
115
|
const table = useTable({
|
|
@@ -128,10 +136,10 @@ export function DependencyTable({
|
|
|
128
136
|
try {
|
|
129
137
|
const res = await syncDependencyFn({ data: { name } })
|
|
130
138
|
if (res.success) {
|
|
131
|
-
toast.success(
|
|
139
|
+
toast.success(m.common_dependency_table_toast_updated({ name, version: res.latestVersion ?? "?" }))
|
|
132
140
|
onMutated?.()
|
|
133
141
|
} else {
|
|
134
|
-
toast.error(
|
|
142
|
+
toast.error(m.common_dependency_table_toast_syncFailed({ name, error: res.error ?? m.common_dependency_table_toast_unknown() }))
|
|
135
143
|
}
|
|
136
144
|
} finally {
|
|
137
145
|
setSyncing((s) => {
|
|
@@ -145,18 +153,18 @@ export function DependencyTable({
|
|
|
145
153
|
async function handleSyncAll() {
|
|
146
154
|
const names = filtered.map((r) => r.name)
|
|
147
155
|
if (names.length === 0) {
|
|
148
|
-
toast.info(
|
|
156
|
+
toast.info(m.common_dependency_table_toast_noSyncable())
|
|
149
157
|
return
|
|
150
158
|
}
|
|
151
159
|
setSyncingAll(true)
|
|
152
160
|
try {
|
|
153
161
|
const results = await syncAllDependenciesFn({ data: { names } })
|
|
154
|
-
const ok = results.filter((r) => r.success)
|
|
155
|
-
const fail = results.filter((r) => !r.success)
|
|
156
|
-
if (ok.length) toast.success(
|
|
162
|
+
const ok = results.filter((r: any) => r.success)
|
|
163
|
+
const fail = results.filter((r: any) => !r.success)
|
|
164
|
+
if (ok.length) toast.success(m.common_dependency_table_toast_syncSuccess({ count: ok.length }))
|
|
157
165
|
if (fail.length)
|
|
158
|
-
toast.error(
|
|
159
|
-
description: fail.map((f) => `${f.name}: ${f.error}`).slice(0, 3).join("\n"),
|
|
166
|
+
toast.error(m.common_dependency_table_toast_syncFailedCount({ count: fail.length }), {
|
|
167
|
+
description: fail.map((f: any) => `${f.name}: ${f.error}`).slice(0, 3).join("\n"),
|
|
160
168
|
})
|
|
161
169
|
onMutated?.()
|
|
162
170
|
} finally {
|
|
@@ -171,16 +179,16 @@ export function DependencyTable({
|
|
|
171
179
|
<div className="space-y-4">
|
|
172
180
|
<div className="flex flex-wrap items-center gap-3">
|
|
173
181
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
174
|
-
<Badge variant="outline">{
|
|
182
|
+
<Badge variant="outline">{m.common_dependency_table_stats_watched({ count: watchedCount })}</Badge>
|
|
175
183
|
<Badge variant="outline" className="border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-400">
|
|
176
184
|
<TriangleAlert className="mr-1 size-3" />
|
|
177
|
-
{
|
|
185
|
+
{m.common_dependency_table_stats_outdated({ count: outdatedCount })}
|
|
178
186
|
</Badge>
|
|
179
187
|
</div>
|
|
180
188
|
<div className="ml-auto flex items-center gap-2">
|
|
181
189
|
<Button onClick={handleSyncAll} disabled={syncingAll} variant="outline" size="sm">
|
|
182
190
|
{syncingAll ? <Loader2 className="mr-1 size-3.5 animate-spin" /> : <RefreshCw className="mr-1 size-3.5" />}
|
|
183
|
-
{
|
|
191
|
+
{m.common_dependency_table_syncAll({ count: filtered.length })}
|
|
184
192
|
</Button>
|
|
185
193
|
</div>
|
|
186
194
|
</div>
|
|
@@ -188,7 +196,7 @@ export function DependencyTable({
|
|
|
188
196
|
<div className="flex flex-wrap items-center gap-x-4 gap-y-1.5">
|
|
189
197
|
<div className="flex flex-wrap items-center gap-1.5">
|
|
190
198
|
<CategoryButton active={categoryFilter === "all"} onClick={() => setCategoryFilter("all")}>
|
|
191
|
-
{
|
|
199
|
+
{m.common_dependency_table_allCategory()}
|
|
192
200
|
</CategoryButton>
|
|
193
201
|
{DEPENDENCY_CATEGORIES.map((c) => (
|
|
194
202
|
<CategoryButton
|
|
@@ -207,7 +215,7 @@ export function DependencyTable({
|
|
|
207
215
|
active={statusFilter === s.value}
|
|
208
216
|
onClick={() => setStatusFilter(s.value)}
|
|
209
217
|
>
|
|
210
|
-
{
|
|
218
|
+
{STATUS_FILTER_LABELS[s.value]()}
|
|
211
219
|
</CategoryButton>
|
|
212
220
|
))}
|
|
213
221
|
</div>
|
|
@@ -218,22 +226,22 @@ export function DependencyTable({
|
|
|
218
226
|
<Search className="absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
|
219
227
|
<Input
|
|
220
228
|
value={search}
|
|
221
|
-
onChange={(e) => setSearch(e.target.value)}
|
|
222
|
-
placeholder={
|
|
229
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.target.value)}
|
|
230
|
+
placeholder={m.common_dependency_table_searchPlaceholder()}
|
|
223
231
|
className="h-9 w-56 pl-8"
|
|
224
232
|
/>
|
|
225
233
|
</div>
|
|
226
|
-
<Select value={viewFilter} onValueChange={(v) => setViewFilter(v as "watched" | "all")}>
|
|
234
|
+
<Select value={viewFilter} onValueChange={(v: string) => setViewFilter(v as "watched" | "all")}>
|
|
227
235
|
<SelectTrigger className="h-9 w-32" size="sm">
|
|
228
236
|
<SelectValue />
|
|
229
237
|
</SelectTrigger>
|
|
230
238
|
<SelectContent>
|
|
231
|
-
<SelectItem value="watched">{
|
|
232
|
-
<SelectItem value="all">{
|
|
239
|
+
<SelectItem value="watched">{m.common_dependency_table_viewFilter_watched()}</SelectItem>
|
|
240
|
+
<SelectItem value="all">{m.common_dependency_table_viewFilter_all()}</SelectItem>
|
|
233
241
|
</SelectContent>
|
|
234
242
|
</Select>
|
|
235
243
|
<span className="ml-auto text-xs text-muted-foreground">
|
|
236
|
-
{
|
|
244
|
+
{m.common_pagination_total({ total: filtered.length, page: currentPage, totalPages })}
|
|
237
245
|
</span>
|
|
238
246
|
</div>
|
|
239
247
|
|
|
@@ -262,7 +270,7 @@ export function DependencyTable({
|
|
|
262
270
|
) : (
|
|
263
271
|
<TableRow>
|
|
264
272
|
<TableCell colSpan={columns.length} className="py-10 text-center text-muted-foreground">
|
|
265
|
-
{
|
|
273
|
+
{m.common_dependency_table_empty()}
|
|
266
274
|
</TableCell>
|
|
267
275
|
</TableRow>
|
|
268
276
|
)}
|
|
@@ -275,9 +283,9 @@ export function DependencyTable({
|
|
|
275
283
|
<PaginationContent>
|
|
276
284
|
<PaginationItem>
|
|
277
285
|
<PaginationPrevious
|
|
278
|
-
text={
|
|
286
|
+
text={m.common_pagination_prev()}
|
|
279
287
|
href="#"
|
|
280
|
-
onClick={(e) => {
|
|
288
|
+
onClick={(e: React.MouseEvent) => {
|
|
281
289
|
e.preventDefault()
|
|
282
290
|
table.previousPage()
|
|
283
291
|
}}
|
|
@@ -288,9 +296,9 @@ export function DependencyTable({
|
|
|
288
296
|
{renderPageItems(currentPage, totalPages, (p) => table.setPageIndex(p - 1))}
|
|
289
297
|
<PaginationItem>
|
|
290
298
|
<PaginationNext
|
|
291
|
-
text={
|
|
299
|
+
text={m.common_pagination_next()}
|
|
292
300
|
href="#"
|
|
293
|
-
onClick={(e) => {
|
|
301
|
+
onClick={(e: React.MouseEvent) => {
|
|
294
302
|
e.preventDefault()
|
|
295
303
|
table.nextPage()
|
|
296
304
|
}}
|
|
@@ -342,7 +350,7 @@ function renderPageItems(
|
|
|
342
350
|
<PaginationLink
|
|
343
351
|
href="#"
|
|
344
352
|
isActive={p === current}
|
|
345
|
-
onClick={(e) => {
|
|
353
|
+
onClick={(e: React.MouseEvent) => {
|
|
346
354
|
e.preventDefault()
|
|
347
355
|
setPageIndex(p - 1)
|
|
348
356
|
}}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { useState } from "react"
|
|
19
|
-
import {
|
|
19
|
+
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import { toast } from '@/components/ui/sonner'
|
|
21
21
|
import { Loader2, Pencil } from "lucide-react"
|
|
22
22
|
import {
|
|
@@ -53,7 +53,6 @@ export function EditDependencyDialog({
|
|
|
53
53
|
sortOrder: row.sortOrder,
|
|
54
54
|
})
|
|
55
55
|
const [saving, setSaving] = useState(false)
|
|
56
|
-
const { t } = useTranslation('common')
|
|
57
56
|
|
|
58
57
|
async function handleSave() {
|
|
59
58
|
setSaving(true)
|
|
@@ -71,11 +70,11 @@ export function EditDependencyDialog({
|
|
|
71
70
|
sortOrder: form.sortOrder,
|
|
72
71
|
},
|
|
73
72
|
})
|
|
74
|
-
toast.success(
|
|
73
|
+
toast.success(m.common_dependency_edit_saved())
|
|
75
74
|
setOpen(false)
|
|
76
75
|
onSaved?.()
|
|
77
76
|
} catch (e) {
|
|
78
|
-
toast.error(
|
|
77
|
+
toast.error(m.common_dependency_edit_saveFailed(), { description: e instanceof Error ? e.message : String(e) })
|
|
79
78
|
} finally {
|
|
80
79
|
setSaving(false)
|
|
81
80
|
}
|
|
@@ -89,7 +88,7 @@ export function EditDependencyDialog({
|
|
|
89
88
|
<Pencil className="size-3.5 text-muted-foreground/50" />
|
|
90
89
|
</span>
|
|
91
90
|
</TooltipTrigger>
|
|
92
|
-
<TooltipContent>{
|
|
91
|
+
<TooltipContent>{m.common_dependency_edit_untracked()}</TooltipContent>
|
|
93
92
|
</Tooltip>
|
|
94
93
|
)
|
|
95
94
|
}
|
|
@@ -97,42 +96,42 @@ export function EditDependencyDialog({
|
|
|
97
96
|
return (
|
|
98
97
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
99
98
|
<DialogTrigger asChild>
|
|
100
|
-
<Button size="sm" variant="ghost" className="size-7 p-0" title={
|
|
99
|
+
<Button size="sm" variant="ghost" className="size-7 p-0" title={m.common_common_edit()}>
|
|
101
100
|
<Pencil className="size-3.5" />
|
|
102
101
|
</Button>
|
|
103
102
|
</DialogTrigger>
|
|
104
103
|
<DialogContent className="max-w-md">
|
|
105
104
|
<DialogHeader>
|
|
106
|
-
<DialogTitle>{
|
|
105
|
+
<DialogTitle>{m.common_dependency_edit_dialogTitle({ name: row.name })}</DialogTitle>
|
|
107
106
|
</DialogHeader>
|
|
108
107
|
<div className="space-y-3">
|
|
109
108
|
<div className="space-y-1.5">
|
|
110
|
-
<Label>{
|
|
111
|
-
<Input value={form.displayName} onChange={(e) => setForm({ ...form, displayName: e.target.value })} />
|
|
109
|
+
<Label>{m.common_dependency_edit_displayName()}</Label>
|
|
110
|
+
<Input value={form.displayName} onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm({ ...form, displayName: e.target.value })} />
|
|
112
111
|
</div>
|
|
113
112
|
<div className="space-y-1.5">
|
|
114
|
-
<Label>{
|
|
113
|
+
<Label>{m.common_dependency_edit_description()}</Label>
|
|
115
114
|
<Textarea
|
|
116
115
|
value={form.description}
|
|
117
|
-
onChange={(e) => setForm({ ...form, description: e.target.value })}
|
|
118
|
-
placeholder={
|
|
116
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setForm({ ...form, description: e.target.value })}
|
|
117
|
+
placeholder={m.common_dependency_edit_descriptionPlaceholder()}
|
|
119
118
|
rows={2}
|
|
120
119
|
/>
|
|
121
120
|
</div>
|
|
122
121
|
<div className="space-y-1.5">
|
|
123
|
-
<Label>{
|
|
122
|
+
<Label>{m.common_dependency_edit_homepage()}</Label>
|
|
124
123
|
<Input
|
|
125
124
|
value={form.homepage}
|
|
126
|
-
onChange={(e) => setForm({ ...form, homepage: e.target.value })}
|
|
125
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm({ ...form, homepage: e.target.value })}
|
|
127
126
|
placeholder="https://"
|
|
128
127
|
/>
|
|
129
128
|
</div>
|
|
130
129
|
<div className="grid grid-cols-2 gap-3">
|
|
131
130
|
<div className="space-y-1.5">
|
|
132
|
-
<Label>{
|
|
131
|
+
<Label>{m.common_dependency_edit_category()}</Label>
|
|
133
132
|
<Select
|
|
134
133
|
value={form.category}
|
|
135
|
-
onValueChange={(v) => setForm({ ...form, category: v as DependencyCategory })}
|
|
134
|
+
onValueChange={(v: string) => setForm({ ...form, category: v as DependencyCategory })}
|
|
136
135
|
>
|
|
137
136
|
<SelectTrigger size="sm">
|
|
138
137
|
<SelectValue />
|
|
@@ -147,10 +146,10 @@ export function EditDependencyDialog({
|
|
|
147
146
|
</Select>
|
|
148
147
|
</div>
|
|
149
148
|
<div className="space-y-1.5">
|
|
150
|
-
<Label>{
|
|
149
|
+
<Label>{m.common_dependency_edit_priority()}</Label>
|
|
151
150
|
<Select
|
|
152
151
|
value={form.priority}
|
|
153
|
-
onValueChange={(v) => setForm({ ...form, priority: v as ThirdPartyDependencyRow["priority"] })}
|
|
152
|
+
onValueChange={(v: string) => setForm({ ...form, priority: v as ThirdPartyDependencyRow["priority"] })}
|
|
154
153
|
>
|
|
155
154
|
<SelectTrigger size="sm">
|
|
156
155
|
<SelectValue />
|
|
@@ -170,28 +169,28 @@ export function EditDependencyDialog({
|
|
|
170
169
|
type="checkbox"
|
|
171
170
|
id="isWatched"
|
|
172
171
|
checked={form.isWatched}
|
|
173
|
-
onChange={(e) => setForm({ ...form, isWatched: e.target.checked })}
|
|
172
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm({ ...form, isWatched: e.target.checked })}
|
|
174
173
|
className="size-4"
|
|
175
174
|
/>
|
|
176
|
-
<Label htmlFor="isWatched">{
|
|
175
|
+
<Label htmlFor="isWatched">{m.common_dependency_edit_watched()}</Label>
|
|
177
176
|
</div>
|
|
178
177
|
<div className="space-y-1.5">
|
|
179
|
-
<Label>{
|
|
178
|
+
<Label>{m.common_dependency_edit_notes()}</Label>
|
|
180
179
|
<Textarea
|
|
181
180
|
value={form.notes}
|
|
182
|
-
onChange={(e) => setForm({ ...form, notes: e.target.value })}
|
|
183
|
-
placeholder={
|
|
181
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setForm({ ...form, notes: e.target.value })}
|
|
182
|
+
placeholder={m.common_dependency_edit_notesPlaceholder()}
|
|
184
183
|
rows={3}
|
|
185
184
|
/>
|
|
186
185
|
</div>
|
|
187
186
|
</div>
|
|
188
187
|
<DialogFooter>
|
|
189
188
|
<Button variant="outline" onClick={() => setOpen(false)} disabled={saving}>
|
|
190
|
-
{
|
|
189
|
+
{m.common_common_cancel()}
|
|
191
190
|
</Button>
|
|
192
191
|
<Button onClick={handleSave} disabled={saving}>
|
|
193
192
|
{saving && <Loader2 className="mr-1 size-3.5 animate-spin" />}
|
|
194
|
-
{
|
|
193
|
+
{m.common_button_save()}
|
|
195
194
|
</Button>
|
|
196
195
|
</DialogFooter>
|
|
197
196
|
</DialogContent>
|
|
@@ -80,7 +80,7 @@ export function BookmarkFormDialog({ open, onOpenChange, item, onSuccess }: Book
|
|
|
80
80
|
if (item) {
|
|
81
81
|
setLoading(true)
|
|
82
82
|
adminGetBookmarkDetailFn({ data: { id: item.id } })
|
|
83
|
-
.then((d) => {
|
|
83
|
+
.then((d: any) => {
|
|
84
84
|
setForm({
|
|
85
85
|
title: d.title ?? '',
|
|
86
86
|
url: d.url ?? '',
|
|
@@ -89,7 +89,7 @@ export function BookmarkFormDialog({ open, onOpenChange, item, onSuccess }: Book
|
|
|
89
89
|
tags: ((d.tags as string[]) ?? []).join(', '),
|
|
90
90
|
})
|
|
91
91
|
})
|
|
92
|
-
.catch((e) => toast.error(e instanceof Error ? e.message : '加载失败'))
|
|
92
|
+
.catch((e: unknown) => toast.error(e instanceof Error ? e.message : '加载失败'))
|
|
93
93
|
.finally(() => setLoading(false))
|
|
94
94
|
} else {
|
|
95
95
|
setForm(EMPTY)
|
|
@@ -157,7 +157,7 @@ export function BookmarkFormDialog({ open, onOpenChange, item, onSuccess }: Book
|
|
|
157
157
|
<Label>标题 *</Label>
|
|
158
158
|
<Input
|
|
159
159
|
value={form.title}
|
|
160
|
-
onChange={(e) => setForm((f) => ({ ...f, title: e.target.value }))}
|
|
160
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, title: e.target.value }))}
|
|
161
161
|
placeholder="网站名称"
|
|
162
162
|
/>
|
|
163
163
|
</div>
|
|
@@ -166,7 +166,7 @@ export function BookmarkFormDialog({ open, onOpenChange, item, onSuccess }: Book
|
|
|
166
166
|
<Input
|
|
167
167
|
type="url"
|
|
168
168
|
value={form.url}
|
|
169
|
-
onChange={(e) => setForm((f) => ({ ...f, url: e.target.value }))}
|
|
169
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, url: e.target.value }))}
|
|
170
170
|
placeholder="https://"
|
|
171
171
|
/>
|
|
172
172
|
<p className="text-xs text-muted-foreground">
|
|
@@ -177,7 +177,7 @@ export function BookmarkFormDialog({ open, onOpenChange, item, onSuccess }: Book
|
|
|
177
177
|
<Label>分类 ID</Label>
|
|
178
178
|
<Input
|
|
179
179
|
value={form.categoryId}
|
|
180
|
-
onChange={(e) => setForm((f) => ({ ...f, categoryId: e.target.value }))}
|
|
180
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, categoryId: e.target.value }))}
|
|
181
181
|
placeholder="可选,留空为未分类"
|
|
182
182
|
/>
|
|
183
183
|
</div>
|
|
@@ -185,7 +185,7 @@ export function BookmarkFormDialog({ open, onOpenChange, item, onSuccess }: Book
|
|
|
185
185
|
<Label>摘要</Label>
|
|
186
186
|
<Textarea
|
|
187
187
|
value={form.summary}
|
|
188
|
-
onChange={(e) => setForm((f) => ({ ...f, summary: e.target.value }))}
|
|
188
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setForm((f) => ({ ...f, summary: e.target.value }))}
|
|
189
189
|
rows={2}
|
|
190
190
|
placeholder="一句话简介"
|
|
191
191
|
/>
|
|
@@ -194,7 +194,7 @@ export function BookmarkFormDialog({ open, onOpenChange, item, onSuccess }: Book
|
|
|
194
194
|
<Label>标签(逗号分隔)</Label>
|
|
195
195
|
<Input
|
|
196
196
|
value={form.tags}
|
|
197
|
-
onChange={(e) => setForm((f) => ({ ...f, tags: e.target.value }))}
|
|
197
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setForm((f) => ({ ...f, tags: e.target.value }))}
|
|
198
198
|
placeholder="如:工具, AI"
|
|
199
199
|
/>
|
|
200
200
|
</div>
|